Python Web Flask – Raspberry Pi 3/4

En travail. Des diagrammes Fritzing et des copies écrans seront ajoutés.  
J’ai beaucoup de soucis avec le formatage. C’est très peu efficace et je suis revenu avec des blocs classiques.

[datemodif]

Raspberry Pi 3 B

Cet article est consacré à l’installation et l’utilisation d’un serveur Web Flask pour le Raspberry Pi. En même temps, il pourrait permettre aux lecteurs qui n’ont presque aucune notion en programmation Python de se familiariser avec ce langage. De petits exemples simples en Python, soit pour le framework Flask, soit pour contrôler les broches GPIO du Raspberry Pi, seront présentés et expliqués ici.

L’installation a été faite sur un Raspberry Pi 4, car je l’avais reçu récemment, en Juillet 2019, à sa sortie. Son utilisation sur un Raspberry Pi 3 B est identique.

Je répéterai ici la définition de Flask qu’on trouve sur Wikipedia:

Flask est un framework open-source de développement web en Python. Son but principal est d’être léger, afin de garder la souplesse de la programmation Python, associé à un système de templates. Il est distribué sous licence BSD2.

Nous assumerons ici que le lecteur a déjà son Raspberry Pi installé avec le système d’exploitation standard Raspbian et qu’il a déjà un peu d’expérience avec les broches GPIO, des capteurs ou autres matériels classiques d’un environnement Raspberry Pi.

Comme j’utilise mes Raspberry Pi sans écran ni clavier, toutes les procédures d’installation et de vérification sont exécutées dans un console du système d’exploration, le Raspian Buster ici. La console apparaît chez moi à partir d’une connexion PuTTY sur un PC Windows 10. Tous les tests de pages accessibles sur le serveur Web Flask sont faites depuis un navigateur Chrome ou Firefox (mais doivent fonctionner aussi avec par exemple Edge ou Opera). L’application Windows PuTTY peut être installée après téléchargement depuis https://www.putty.org/.

Installation de Flask

 login as: pi
pi@192.168.1.143's password:
Linux raspberrypi 4.19.50-v7l+ #895 SMP Thu Jun 20 16:03:42 BST 2019 armv7l
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Sep 6 09:07:26 2019 from 192.168.1.110
pi@raspberrypi:~ $

L’adresse IP 192.168.1.143 du Raspberry Pi 4 a été définie lors de l’installation et attribuée comme adresse fixe dans le routeur domestique.

Si nous travaillions depuis un PC Linux, par exemple Ubuntu, nous pourrions alors utiliser la commande ssh équivalente à l’application Windows PuTTY :

ssh pi@192.168.1.143

Si notre Raspberry Pi n’a pas été installé récemment, il faudrait tout d’abord le mettre à jour. Ces deux commandes peuvent être exécutées de toute manière, sans risque, pour des mises à jour éventuelles :

pi@raspberrypi ~ $ sudo apt-get update
pi@raspberrypi ~ $ sudo apt-getupgrade

Les deux commandes d’installation requises pour Flask sont :

sudo apt-get install python-pip
sudo pip install flask

Nous indiquerons aussi que le langage Python est préinstallé et qu’il est tout de même conseillé de l’avoir déjà un peu utilisé. Pour les programmeurs du Raspberry Pi, c’est le langage essentiel, en particulier pour développer des scripts utilisant les broches GPIO et du matériel comme des capteurs de température, de mouvement, ou autres.

Dans mon cas nous voyons que l’utilitaire pip, un gestionnaire de paquets pour Python, ainsi que Flask sont déjà installés :

pi@raspberrypi:~ $ sudo apt-get install python-pip
Reading package lists... Done
Building dependency tree
Reading state information... Done
python-pip is already the newest version (18.1-5+rpt1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
pi@raspberrypi:~ $ sudo pip install flask
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: flask in /usr/lib/python2.7/dist-packages (1.0.2)
pi@raspberrypi:~ $

Première utilisation

Nous assumerons que le lecteur à suffisamment de connaissance avec Linux et nous commencerons donc par créer un répertoire de travail pour nos scripts Python dédié à Flask :

pi@raspberrypi:~ $ mkdir flaskeur
pi@raspberrypi:~ $ cd flaskeur
pi@raspberrypi:~/flaskeur $

Nous allons créer un premier script Flask nommé salut_flaskeur.py comme ceci :

salut_flaskeur.py
from flask import Flask
app = Flask(name)
@app.route('/')
def salut_flaskeur():
return "Salut Flaskeur!"
if name == 'main':
app.run(host='0.0.0.0',port=5000, debug=True)

La fonction salut_flaskeur() est définie par le mot clé def. Elle va simplement nous retourner le texte non formaté Salut Flaskeur et ceci pour le document racine de notre serveur Web qui est défini avec @app.route(‘/’).

Si nous définissions un @app.route(‘/salut’), nous devrions alors entrer http://192.168.1.143:5000/salut (voir ci-dessous) et un http://192.168.1.143:5000 nous retournerait un Not Found comme tous autres URL qui n’est pas un simple /salut. Le code d’erreur 404, qui correspond à une page non trouvée, sera présente dans la console.

Les autres instructions indiquent que c’est une application Flask a démarrer avec une entrée main() classique de Python.

Nous utiliserons le host 0.0.0.0 afin de configurer le serveur Web Flask pour être visible au travers du réseau. Les sites de présentation de Flask pour le Pi utilise souvent un simple app.run(debug=True) qui nécessiterait de travailler en mode écran avec clavier et souris, ou en mode VNC. Dans ce cas uniquement un accès http://127.0.0.1:5000 sur la propre machine serait possible, donc avec par exemple le navigateur chromium-browser préinstallé sur le Pi 4, navigateur que je n’ai jamais utilisé.

Attention à l’indentation correcte (sur deux des lignes ici ou j’ai choisis 3 espaces) qui est nécessaire pour le langage Python,

Travaillant moi-même sous Linux bien avant l’arrivée de l’éditeur GNU nano en l’an 2000, je suis plus habitué à utiliser l’éditeur vi. Mais, dans une console, nous pouvons très bien travailler avec l’éditeur nano, qui est traditionnellement utilisé, et avec la commande :

nano salut_flaskeur.py

Un Ctrl-C depuis Windows du code ci-dessus, suivi d’un Ctlr-O ou/et Ctrl-X, seront nécessaires pour sauver le code et quitter. La commande

cat salut_flaskeur.py

nous permettra de vérifier que le contenu est correct avant d’exécuter :

python salut_flaskeur.py

qui nous donnera ceci :

pi@raspberrypi:~/flaskeur $ python salut_flaskeur.py
 * Serving Flask app "flaskeur" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 108-533-113

Pour interrompre le serveur un Ctrl-C sera nécessaire.

Nous indiquerons aussi que nous utilisons la version 2 de Python, identifiable avec python :

pi@raspberrypi:~/flaskeur $ python -V
Python 2.7.16

Pour démarrer le serveur Web Flask en arrière plan et identifier qu’il est effectivement actif avec la commande ps qui montre les processus actifs :

pi@raspberrypi:~/flaskeur $ python salut_flaskeur.py &
[1] 2211
pi@raspberrypi:~/flaskeur $  * Serving Flask app "flaskeur" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 108-533-113
ps
  PID TTY          TIME CMD
  711 pts/0    00:00:00 bash
 2211 pts/0    00:00:00 python
 2213 pts/0    00:00:00 python
 2216 pts/0    00:00:00 ps

La commande :

sudo kill -9 2211 2213

nous permettra de stopper les deux processus concernés, c’est à dire le serveur Web.

Démarrage automatique

Au lancement du Raspberry Pi, il est possible d’indiquer que notre Web serveur tournera automatiquement, sans besoin d’une console pour le démarrer. Notre salut_flaskeur.py est plus que primitif et il est a espéré que le lecteur s’attaquera à la suite de cet article.

Avec la commande:

sudo nano /etc/rc.local

nous ajouterons le lancement en arrière plan d’ httpserveur5000.sh :ss


Nous allons créer un premier script Flask nommé salut_flaskeur.py comme ceci :

……………………
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
/home/pi/flaskeur/httpserveur5000.sh &
exit 0

Nous devrons créer le script bash httpserveur5000.sh avec :

pi@raspberrypi:~/flaskeur $ nano httpserveur5000.sh

et contenant ici les deux commandes:

cd /home/pi/flaskeur
python salut_flaskeur.py

Le cd n’est pas ici nécessaire, mais plus tard, nous utiliserons dans nos script Flask d’autres ressource venant de ce répertoire. Nous mettrons aussi les droits correctement, testerons le script bash, l’interromprons avec Ctrl-C, avant de redémarrer le Pi avec un traditionnel sudo reboot :

chmod +x httpserveur5000.sh
/home/pi/flaskeur/httpserveur5000.sh

Premier test depuis notre PC

Nous utiliserons Chrome, Firefox ou notre navigateur Internet depuis notre PC avec l’adresse http://192.168.1.143:5000/. Nous verrons alors apparaître le texte :

Salut Flaskeur!

Voilà ! C’est un bon début et cela fonctionne. Nous pourrions déjà ici modifier le port 5000, voir le texte ou encore définir un autre port et un autre fichier salut2_flaskeur.py. Les deux serveurs Web pourrait être lancés simultanément, en parallèle, nous permettant de les avoir actifs en même temps, par exemple pour des fonctionnalités différentes.

Templates

Le terme template ici est utilisé pour indiquer que nos script Python Flask vont être définis pour pouvoir définir une jolie page écrite en HTML contenant des parties variables.

Nous allons tout d’abord écrire un script Python Flask pour présenter l’information générale sur notre Raspberry Pi. Pour commencer il faut créer un sous-répertoire qui contiendra le fichier html utiliser par cette technologie de templates :

pi@raspberrypi:~ $ pwd
/home/pi
pi@raspberrypi:~ $ cd flaskeur/
pi@raspberrypi:~/flaskeur $ mkdir templates

Moi-même j’aime bien travailler sur mon PC Windows. J’utilise l’éditeur Notepad++, vraiment cool et je l’associe à WinScp et PuTTY. Expliquer avec WinScp ….. télécharger … etc … et utilisation de Nodepad++ PuTTY. Pour cet article j’ai défini un répertoire D:\RaspberryPi4\PythonFlask sur mon PC Windows. Ce répertoire est automatiquement ouvert dans WinScp car défini une fois dans sa configuration.

Je préfère Notepad++ sur le PC à nano sur le Raspberry Pi (rappel : je travaille sans écran ni clavier, ni avec un client VNC). Cette manière de faire me permet de posséder automatiquement une sauvegarde de mon code sur le Pi. Pour de petite corrections, j’utilise de préférence vi que je maîtrise depuis plus de 30 ans. Je ne dois pas oublier de repasser le code sur le PC avec WinScp, en sélectionnant tous les fichiers et sous-répertoire de flaskeur, les dates me montrant d’ailleurs si les fichiers sont indentiques.

Dans le répertoire flaskeur, nous allons donc écrire un fichier system.py contenant :

from flask import Flask, render_template
import platform
app = Flask(__name__)

@app.route("/system")
def hello():
   uname = platform.uname()
   elt1 = uname[0]
   elt2 = uname[0]
   elt3 = uname[0]
   elt4 = uname[0]
   elt5 = uname[0]
   templateData = {
      'title' : 'System info!',
      'os': elt1
      'machine': elt2
      'version': elt3
      'date': elt4
      'cpu': elt5
      }
   return render_template('system.html', **templateData)

if __name__ == "__main__":
   app.run(host='0.0.0.0', port=80, debug=True)

Dans le répertoire templates nous déposerons le fichier system.html:

<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
   </head>

   <body>
      <h1>Hello, World!</h1>
      <h2>The date and time on the server is: {{ time }}</h2>
   </body>
</html>

Expliquer et en particulier les {} …

Pour démarrer le server Web Flask un sudo est ici nécessaire, car nous avons utilisé le port standard 80 :

sudo python system.py

Raspberry Pi info!
Operating system: Linux
Machine: raspberrypi
Version: 4.19.50-v7l+
Date: #895 SMP Thu Jun 20 16:03:42 BST 2019
CPU: armv7l

Un & additionnel pour un démarrage en arrière plan est possible évidemment.

Schéma Fritzing – Relais et Buzzer

Le schéma Fritzing présenté ici est dédié au Raspberry Pi. Nous noterons que les broches des Raspberry Pi 3 ou Pi 4 sont identiques.

Explication en détails à faire encore ….

En utilisant deux broches de terre différentes (GND), nous n’aurons pas de platine d’essai. Le buzzer, le relais et le Raspberry Pi ayant des broches mâles, il suffit de se procurer des fils avec deux connecteurs femelles. J’utilise volontiers des fils de 20 cm qui me permettent, en particulier pour le buzzer, de placer mes composants assez éloignés du boîtier contenant le Raspberry Pi. Je n’indique pas ici comme connecter le relais à la partie haute tension, procédure qui nécessitera les précautions nécessaires voire une vérification par un électricien.

Déclencher un relais sur le Raspberry Pi

Nous allons à présent faire un exercice avec un relais que nous allons enclencher ou déclencher par du logiciel qui sera extérieur au serveur Flask Web et aussi écrit en langage Python :

pi@raspberrypi:~/flaskeur $ cat relayonoff.py
# coding: utf-8
import RPi.GPIO as GPIO
import time

RelayPin = 16    # broche physique 16 

def setup():
  GPIO.setmode(GPIO.BOARD)         # Numéro GPIO par broche physique
  GPIO.setup(RelayPin, GPIO.OUT)   # RelayPin en mode output

  try:
    while True:
      GPIO.output(RelayPin, GPIO.HIGH)
      f = open("relaystate.txt", "w")
      f.write("ON")
      f.close()
      time.sleep(4)

      GPIO.output(RelayPin, GPIO.LOW)

      f = open("relaystate.txt", "w")
      f.write("OFF")
      f.close()

      time.sleep(4)
  except KeyboardInterrupt:  # Interruption avec 'Ctrl+C'
     GPIO.cleanup()                   # Ressources libérées

if __name__ == '__main__':   # Démarrage en Python
  setup()

Le script Python va ouvrir et fermer le relais, chaque 10 secs, et stocker l’état dans un fichier nommer relaystate.txt. Le contenu de ce dernier n’a rien de plus que 2 ou 3 caractères et cela va simplifier la génération de la page HTML. Pour connaître l’état du relais, il n’est pas possible d’ouvrir cette broche GPIO en GPIO.IN. C’est la raison d’utiliser ici un fichier qui est un mécanisme simple.

Le script Python Flask relaystate.py se présentera ainsi :

from flask import Flask, render_template
import datetime
app = Flask(__name__)

@app.route("/relaystate")
def hello():
   file = open("relaystate.txt", "r") 
   state = file.readline()
   
   templateData = {
      'title' : 'Relay state!',
      'relaystate': state
      }
   return render_template('relaystate.html', **templateData)

if __name__ == "__main__":
   app.run(host='0.0.0.0', port=80, debug=True)

Avec son template relaystate.html :

<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
   </head>

   <body>
      <h3>Relay state: {{ relaystate }}</h2>
   </body>
</html>

C’est incroyablement simple, il faut le reconnaître. Suivant l’état du relais nous aurons une réponse pour http://192.168.1.143/relaystate tel que :

Relay state: ON

Expliquer : autre HTML et css !

Pour pouvoir visionner cette page il nous faudra les deux commandes :

 python relayonoff.py &
 sudo python relaystate.py &

Nous noterons ici qu’à chaque lancement de processus avec &, un message apparaît avec le numéro du process entre crochet. Il est possible de l’utiliser pour la commande sudo kill -9. Pour relayonoff.py, le kill ne va pas exécuter GPIO.cleanup(). Nous pourrions alors avoir au prochain démarrage un

relayonoff.py:9: RuntimeWarning: This channel is already in use, continuing anyway.  
Use GPIO.setwarnings(False) to disable warnings.
GPIO.setup(RelayPin, GPIO.OUT)   # RelayPin en mode output

qui indique que la ressource est utilisée. Le script fonctionnera quand même et le lecteur pourrait rajouter un

GPIO.setwarnings(False) 

au début de def setup().

Avant de continuer la lecture la lecture de cet article ou de modifier la présentation de cette page livrée par le serveur Flask Web, il nous faudra stopper les deux processus concernés. Une commande ps nous montrera les 3 processus à stopper éventuellement avec un sudo kill -9.

Un exemple Flask avec POST

Avant de passer à un exemple avec GET, nous commencerons par un exercice impliquant un ESP32 qui va détecter la présence du passage d’un chat, envoyer l’événement sur un Raspberry Pi et avant de générer un son variable ultrasonique répulsif.

Nous commencerons par le script Flask correspondant à notre GET désiré :

from flask import Flask
import time
import RPi.GPIO as GPIO
app = Flask(name)
@app.route("/beep")
def beep():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 500)
p.start(0)
for dc in range(0, 101, 5):
p.ChangeDutyCycle(dc/2)
p.ChangeFrequency(50+(2*dc))
time.sleep(0.15)
p.stop()
GPIO.cleanup()
return ''
if name == "main":
app.run(host='0.0.0.0', port=8002, debug=True)

Ce script n’a pas de template, c’est un simple POST du protocol HTTP. Il sera déposé dans le répertoire flaskeur et démarré avec :

pi@raspberrypi:~/flaskeur $ python beepalarm.py

Un sudo n’est pas nécessaire pour le port 8002. Un test simple se fera avec

http://192.168.1.143:8002/beep

dans un navigateur, où aucun retour visible ne sera présenté. Il ne serait pas possible de tester un POST de cette manière.

Nous présentons à présent la partie ESP (donner le modèle) avec un script esp32buzzerchat.ino développé avec l’IDE de l’Arduino et le type de carte ESP32 Dev Module  :

Expliquer ici …

#include <WiFi.h>
#include <HTTPClient.h>

//13.5KHZ-19.5KHZ : Efficace pour repousser les animaux comme les souris, les rats, les chiens, les renards, les martres (belette) etc.
//19.5KHZ-24.5KHZ : Efficace pour repousser les animaux tels que les chats, les ratons laveurs, les blaireaux, les mouffettes etc.

int freq = 2000;
int channel = 0;
int pinPir = 13;
int resolution = 8;
int pinBuzzer = 12;

int httpCode = 1000;

const char* ssid = "....";
const char* pwd = "....";

void setup() {
Serial.begin(9600);

Serial.print("Connexion à ");
Serial.println(ssid);
WiFi.begin(ssid, pwd);
while (WiFi.status() != WL_CONNECTED) {
delay(200);
Serial.print(".");
}
//Montre l'adresse IP
Serial.println("");
Serial.println("WiFi connecté!");
Serial.println("Adresse IP: ");
Serial.println(WiFi.localIP());

ledcSetup(channel, freq, resolution);
ledcAttachPin(pinBuzzer, channel);

pinMode(pinPir,INPUT);
}

void loop() {
bool isDetected = digitalRead(pinPir);

if (isDetected){
Serial.println("Présence détectée");

if (httpCode > 0) {
HTTPClient http;
http.begin("http://192.168.1.143:8002/beep");
http.addHeader("Content-Type", "text/plain");
httpCode = http.GET();
Serial.print("http return: ");
Serial.println(httpCode);
http.end(); //free resources
}

delay(10000);
Serial.println("Start beep");
ledcWrite(channel, 255);

int freq = 17000; //Hertz
while (freq < 24000) {
ledcWriteTone(channel, freq);
freq += 100;
delay(80);
}
while (freq > 17000) {
ledcWriteTone(channel, freq);
freq -= 100;
delay(60);
}

ledcWriteTone(channel, 0);
Serial.println("End of beep");

delay(100);
}

delay(100);
}

Schéma Fritzing – ESP32 – PIR et buzzer

Le schéma Fritzing correspondant au sketch esp32buzzerchat.ino ci-dessus se présente ainsi :

Le script Flask devra être actif sur le Raspberry Pi pour recevoir l’événement.
A suivre ….. une description complète.

Un exemple Flask de reboot du Raspberry Pi

Nous restons dans le même contexte d’un POST traditionnel avec le script que j’ai nommé reboot.py:

from flask import Flask
import os
app = Flask(__name__)

@app.route("/reboot")
def reboot():
#os.system('sudo reboot') fonctionne aussi
os.system('sudo shutdown -r now')

return ''

if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)

Il est possible de choisir un autre port ou d’intégrer ce /reboot dans un script plus complexe contenant d’autres entrées. Un serveur Web séparé sur 8080 et démarré en parallèle avec d’autres serveurs ou applications est une solution que j’ai adoptée où un
http://192.168.1.143:8002/reboot
relancerait le Raspberry Pi.