Raspberry Pi OS version = 32-bit Bullseye (debian 11)
Sur Bullseye, le driver de la PiCam a été remplacé par libcamera
, qui est moins ouvert et incompatible avec les applications raspicam
et le module Python Picamera
. Ce module est remplacé par Picamera2
mais sur Bullseye 32-bit.
Installer la camera dans Raspberry Pi OS
Se connecter en tant qu’utilisateur
pi
au terminal du Raspberry.
Comment vérifier que Raspberry Pi OS a bien détecté la caméra
Taper dans le terminal :
vcgencmd get_camera
- Si la caméra n’est pas installée, la sortie est :
supported=0 detected=0
- Si la caméra est installée, la sortie est :
supported=1 detected=1
et par conséquent, vous pouvez passer à l’étape suivante : Installer le module Pythonpicamera
Comment installer la caméra dans Raspberry Pi OS
D’abord, il faut mettre à jour les dépendances de l’OS :
sudo apt-get update
Puis, pour installer la caméra, il faut lancer l’assistant de configuration :
sudo raspi-config
D’abord, on met à jour, cet assistant de configuration :
Puis on installe le module camera :
Normalement le menu va proposer un reboot, sinon le faire manuellement en tapant sudo reboot
Lorsque le Raspberry Pi aura redémarré, vérifier que la caméra est bien supportée par l’OS :
vcgencmd get_camera
Comment vérifier que le format vidéo H.264 est supporté ?
v4l2-ctl --list-formats
Dans la liste des formats vidéo supportés, H264 devrait être présent :
Diffuser le flux vidéo sur un canal RTSP
Le but est de créer un serveur de flux vidéo RTSP afin de :
- visualiser sur un ordi du réseau avec VLC
- ou bien intégrer cette caméra dans un système de surveillance NVR prenant en charge le RTSP.
Se connecter en tant qu’utilisateur
pi
au terminal du Raspberry.
Installation du serveur RTSP v4l2 v4l2rtspserver
On va installer v4l2rtspserver
, un serveur RTSP pour l’API v4l2 (video for Linux 2) acceptant les formats H264, HEVC, JPEG, VP8, VP9. On clone un projet Github puis on installe :
cd /home/pi
sudo apt-get -y install cmake liblog4cpp5-dev libv4l-dev git
git clone https://github.com/mpromonet/v4l2rtspserver.git
cd v4l2rtspserver/
cmake .
make
sudo make install
Pour tester lancer la diffusion du flux vidéo d’une image 1296 x 972 @ 15 fps sur le port 8554 :
v4l2rtspserver -H 972 -W 1296 -F 15 -P 8554 /dev/video0
Regarder le flux vidéo RTSP
Sur un ordi du réseau, ouvrir VLC > ouvrir un flux réseau.
Renseigner l’URL du Raspberry Pi en utilisant son hostname (ou son adresse ip) sur le port 8554.
(rappel : Si besoin, l’adresse IP est accessible avec l’instruction ifconfig
)
L’adresse a donc la forme suivante :
rtsp://<raspberry-pi-ip>:8554/unicast
Puis faire Ctrl+C pour arrêter la diffusion et récupérer l’invite de commande.
Faire tourner le serveur en tâche de fond
Modifier le service déjà installé
Pour que le processus tourne en tâche de fond, v4l2 a déjà créé un service. On va éditer le fichier :
sudo nano /lib/systemd/system/v4l2rtspserver.service
Et le modifier pour qu’il ressemble à ceci :
[Unit]
Description=V4L2 RTSP server
After=network.target
[Service]
Type=simple
Restart=always
RestartSec=1
User=pi
ExecStart=/usr/local/bin/v4l2rtspserver -H 972 -W 1296 -F 15 -P 8554 /dev/video0
WorkingDirectory=/usr/local/share/v4l2rtspserver
StartLimitIntervalSec=0
[Install]
WantedBy=multi-user.target
Taper Ctrl+O et ENTRER pour sauvegarder, Ctrl+X pour fermer.
Activer ce service en tâche de fond
Pour lancer la diffusion vidéo en tâche de fond :
systemctl start 4l2rtspserver.service
Pour stopper la diffusion vidéo :
systemctl stop 4l2rtspserver.service
Remarque : Si on souhaite que ce service se lance automatiquement à chaque démarrage alors il faut l’ajouter à la séquence de boot en :
- rafraîchissant le daemon
- puis l’ajoutant au boot.
Puis on reboot pour tester.
sudo systemctl daemon-reload
sudo systemctl enable v4l2rtspserver.service
sudo reboot
Après le redémarrage, on peut ouvrir VLC et afficher le flux vidéo sur VLC avec l’URL précédente.
Pour les réglages de la caméra
Par exemple pour faire une rotation de 180° de l’image :
v4l2-ctl --set-ctrl=rotate=180
La commande suivante liste les réglages disponibles, ensuite pour les appliquer il faut utiliser --set-ctrl
.
v4l2-ctl --list-ctrls
Pour en savoir plus : https://github.com/mpromonet/v4l2rtspserver/
Intégration dans un NVR
Par exemple, on peut intégrer ce flux dans un système d’enregistrement de caméra de surveillance NVR, comme QVR d’un nas Qnap :
Alternative
Pour un autre serveur RTSP (en gardant désactivé la Legacy Camera dans raspi-config) :
MediaMTX : https://github.com/bluenviron/mediamtx
Pi Camera & serveur web
Sur Bullseye, il faut utiliser le module picamera2
et désactiver la caméra dans Raspiconfig.
Lien vers le manuel de picamera2
: https://datasheets.raspberrypi.com/camera/picamera2-manual.pdf
Se connecter en tant qu’utilisateur
pi
au terminal du Raspberry.
Installer le module Python picamera2
Comment vérifier si le module picamera2
est déjà présent
Normalement, le module est déjà présent. Pour s’en assurer, taper dans le terminal Bash (l’option -c
signifie command
, en effet on n’exécute qu’une ligne de commande) :
python3 -c "import picamera2"
- Si rien ne s’affiche alors le module est déjà installé : vous pouvez passer à l’étape suivante : Créer un serveur web Python pour le streaming
- Si vous obtenez un message d’erreur alors cela signifie que le module n’est pas présent :
ModuleNotFoundError: No module named 'picamera'
Comment installer le module Python picamera2
On installe pip3 :
sudo apt-get update && sudo apt-get install python3 python3-pip
Puis on installe le module picamera par l’intermédiaire de pip :
pip install --user picamera
Depuis Python 3.11, pip
se heurte à error: externally-managed-environment
. Une solution consiste à désactiver ce fichier (en le renommant) :
sudo mv /usr/lib/python3.11/EXTERNALLY-MANAGED /usr/lib/python3.11/EXTERNALLY-MANAGED.old
Créer un serveur web Python pour le streaming
Le programme Python
Créer un fichier (ne surtout pas le nommer picamera.py
car c’est le nom du module importé) :
nano /home/pi/camera.py
L’éditeur nano s’ouvre, y copier/coller le programme suivant :
# On ajoute au path l'emplacement des modules python
# ce sera utile pour lancer ce programme dès le boot
import sys
sys.path.append('/home/pi/.local/lib/python3.7/site-packages')
import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server
PAGE="""\
<html>
<head>
<title>picamera MJPEG streaming demo</title>
</head>
<body>
<h1>PiCamera MJPEG Streaming Demo</h1>
<img src="stream.mjpg" width="640" height="480" />
</body>
</html>
"""
class StreamingOutput(object):
def __init__(self):
self.frame = None
self.buffer = io.BytesIO()
self.condition = Condition()
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame, copy the existing buffer's content and notify all
# clients it's available
self.buffer.truncate()
with self.condition:
self.frame = self.buffer.getvalue()
self.condition.notify_all()
self.buffer.seek(0)
return self.buffer.write(buf)
class StreamingHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(301)
self.send_header('Location', '/index.html')
self.end_headers()
elif self.path == '/index.html':
content = PAGE.encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content))
self.end_headers()
self.wfile.write(content)
elif self.path == '/stream.mjpg':
self.send_response(200)
self.send_header('Age', 0)
self.send_header('Cache-Control', 'no-cache, private')
self.send_header('Pragma', 'no-cache')
self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
self.end_headers()
try:
while True:
with output.condition:
output.condition.wait()
frame = output.frame
self.wfile.write(b'--FRAME\r\n')
self.send_header('Content-Type', 'image/jpeg')
self.send_header('Content-Length', len(frame))
self.end_headers()
self.wfile.write(frame)
self.wfile.write(b'\r\n')
except Exception as e:
logging.warning(
'Removed streaming client %s: %s',
self.client_address, str(e))
else:
self.send_error(404)
self.end_headers()
class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
allow_reuse_address = True
daemon_threads = True
### Pgm ###
with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
# Si besoin d'une rotation (en degrés) :
# camera.rotation = 90
# Pour insérer un texte :
# camera.annotate_text_size = 50 # réglage facultatif
# camera.annotate_text = "Hello world!"
# Pour changer l'apparence :
# camera.brightness = 70 # dans 0-100 (par défaut = 50)
# camera.contrast = 70 # dans 0-100 (par défaut = 50)
# Pour ajouter un filtre de la liste camera.IMAGE_EFFECTS :
# camera.IMAGE_EFFECTS = ['none', 'negative', 'solarize', 'sketch',
# 'denoise', 'emboss', 'oilpaint', 'hatch', 'gpen', 'pastel',
# 'watercolor', 'film', 'blur', 'saturation', 'colorswap',
# 'washedout', 'posterise', 'colorpoint', 'colorbalance',
# 'cartoon', 'deinterlace1', 'deinterlace2']
# camera.image_effect = 'colorswap'
# Pour régler l'exposition de la liste camera.IMAGE_EFFECTS (par défaut : auto)
# camera.IMAGE_EFFECTS = ['off', 'auto', 'night', 'nightpreview', 'backlight',
# 'spotlight', 'sports', 'snow', 'beach', 'verylong', 'fixedfps', 'antishake',
# 'fireworks']
# camera.exposure_mode = 'beach
# Pour régler la balance des blancs dans la liste camera.AWB_MODES (par défaut : auto) :
# camera.AWB_MODES = ['off', 'auto', 'sunlight', 'cloudy', 'shade', 'tungsten',
# 'fluorescent', 'incandescent', 'flash', 'horizon']
camera.awb_mode = 'tungsten'
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
Taper Ctrl+O et ENTRER pour sauvegarder, Ctrl+X pour fermer.
Pour obtenir l’emplacement des modules python, il suffit de taper dans le terminal pip3 install --user picamera
Puis de copier le chemin après « Requirement already satisfied: »
Récupérer l’adresse IP du Raspberry Pi
Dans le terminal, taper :
ifconfig
Pour se connecter par hostname :
- https://forums.balena.io/t/connecting-to-device-using-hostname-via-wifi/133323
- https://superuser.com/questions/431481/cant-resolve-hostnames-over-lan
Démarrer le serveur de web streaming
Pour démarrer le serveur, exécuter le programme précédent :
python3 camera.py
Sur un autre ordinateur (appelé client), ouvrir un navigateur et se connecter au serveur sur son port 8000. Utiliser son adresse IP ou son nom d’hôte (hostname) http://adresse_IP_du_RPi:8000
http://192.168.0.234:8000
ou
http://raspberrypi0:8000
Exécuter ce script à chaque démarrage
Créer un service
On va ajouter le programme aux fichiers de systemd.
En effet, systemd est un processus qui commande les programmes au moment du boot.
On commence par créer un nouveau fichier Unit
sudo nano /lib/systemd/system/camera.service
L’éditeur nano s’ouvre, y copier/coller le fichier unit suivant :
[Unit]
Description=Mon service de camera streaming
After=multi-user.target
[Service]
Type=idle
ExecStart=/usr/bin/python3 /home/pi/camera.py
[Install]
WantedBy=multi-user.target
Taper Ctrl+O et ENTRER pour sauvegarder, Ctrl+X pour fermer.
Ce fichier commence par définir un nouveau « service » qui sera lancé une fois que l’environnement multi-utilisateur sera disponible After=multi-user.target
Puis ExecStart
donne la ligne de commande à exécuter, donc python3 camera.py
Mais, il faut tout noter en chemin absolu (car c’est le démarrage de linux), cela est valable pour python3
et pour notre script camera.py
Type=idle
signifie que systemd attendra que tous les travaux en cours soient finis avant de lancer le service.
sudo chmod 644 /lib/systemd/system/camera.service
Vérifions avec un affichage long :
Rappel sur la base octale :
Le poids de chaque droit read/write/execute est :
r=100
=4 w=010
=2 x=001
=1.
Donc pour 644 :
- U = 6 =
110
= r + w - G = 4 =
100
= r - O = 4 =
100
= r
Ajouter ce service à la séquence de boot
Maintenant que le service a été créé, on doit :
- rafraîchir le daemon
- puis ajouter le service à la séquence de boot.
Puis on reboot pour tester.
sudo systemctl daemon-reload
sudo systemctl enable camera.service
sudo reboot
Après le redémarrage, on peut ouvrir le navigateur et tester l’IP du Raspberry Pi (ou son hostname) sur le port 8000.
http://raspberrypi0:8000
Installer une camera USB
Il existe plusieurs flux possibles :
- HLS / DASH : c’est un flux H264 avec un bitrate variable, mais il y a un délai de 3s.
HLS est de Apple, DASH est de Chrome. - MJPEG : pas de délai mais gourmande en bande passante, car les images JPEG sont volumineuses.
- H264 : peu de latence et une bande passante raisonnable.
Pour vérifier que la caméra est bien reconnue :
lsusb
Pour connaître les formats vidéo supportées par la caméra :
v4l2-ctl --list-formats
Notre caméra MJPEG
Elle ne possède que le MJPEG, le flux est très lent et très saccadé.
La PiCamera est de bien meilleure qualité.
Installer motion
Installation de motion et ses dépendances (c’est très très long) :
sudo apt-get update
sudo apt-get install -y libwebp-dev ffmpeg libmariadb3 libpq5 libmicrohttpd12
sudo apt-get install -y motion
Modifier la configuration
Editer le fichier /etc/default/motion
sudo nano /etc/default/motion
Mettre start_motion_daemon=yes
au lieu de start_motion_daemon=no
Editer /etc/motion/motion.conf
sudo nano /etc/motion/motion.conf
Mettre ces valeurs :
daemon on
au lieu dedaemon off
width 640
au lieu dewidth 320
height 480
au lieu deheight 240
stream_localhost off
au lieu destream_localhost on
(cette ligne est à la fin du fichier)- Notez le n° du
stream_port
normalement c’est 8081.
Reboot le Raspberry :
sudo reboot
Activer le service
Démarrer le streaming :
sudo systemctl start motion
Puis on peut activer le service au boot :
sudo systemctl enable motion
Installer un dongle Wifi
Voir https://www.electronicshub.org/setup-wifi-raspberry-pi-2-using-usb-dongle/
Description
Modèle : tp-link TL-WN725N Version 3.0
- Vitesse : 150 Mbps
- Wifi N (2.4 GHz) donc 802.11n
Reconnaissance par l’OS
Brancher le dongle puis démarrer le raspberry. Se connecter en tant que pi.
Pour vérifier que le dongle wifi est bien reconnu par l’OS, on va utiliser la commande dmesg
(abréviation de « display message ») car elle affiche les messages de la mémoire tampon du noyau.
Comme ces messages représentent beaucoup beaucoup de lignes, alors on va ajouter un pipe |
et l’option more
pour avoir un affichage page par page.
En appuyant sur la touche ESPACE, on passe d’une page à la suivante.
Sur la dernière page, vous devriez voir le dongle USB : le wifi 802.11n et le fabriquant Realtek.
dmesg | more
Ajouter l’interface wifi
Pour ajouter cette nouvelle interface, on édite le fichier des interfaces :
sudo nano /etc/network/interfaces
Le fichier est possiblement comme ceci :
Puis ajouter ces lignes (si elles ne sont pas déjà présentes) :
Rappel : Pour coller dans Linux, il faut faire un clic droit.
auto lo
iface lo inet loopback
iface eth0 inet manual
auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
Taper Ctrl+O et ENTRER pour sauvegarder, Ctrl+X pour fermer.
Le code du wifi
Puis il faut ajouter les codes de connexion dans le fichier cité ci-dessus :
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
Et ajouter les lignes suivantes en renseignant correctement :
- le SSID : le nom du réseau wifi
- le mot de passe de connexion
country=FR
network={
ssid="nom du réseau wifi"
scan_ssid=1
psk="mot de passe du réseau wifi"
# proto=RSN
key_mgmt=WPA-PSK
# pairwise=CCMP TKIP
# group=CCMP TKIP
# id_str="nom du réseau wifi"
}
Plusieurs réseaux wifi
On peut également insérer plusieurs réseaux wifi et ajouter une priorité (s’ils sont présents simultanément).
country=FR
network={
ssid="réseau1"
psk="mot de passe 1"
# proto=RSN
key_mgmt=WPA-PSK
# pairwise=CCMP TKIP
# group=CCMP TKIP
priority=5
}
network={
ssid="réseau2"
psk="mot de passe 2"
# proto=RSN
key_mgmt=WPA-PSK
# pairwise=CCMP TKIP
# group=CCMP TKIP
priority=2
}
Eteindre et essayer
- On éteint le Raspberry Pi :
sudo poweroff
- On débranche le câble Ethernet
- >> Brancher un écran HDMI pour lire l’adresse IP <<
- On rallume le Raspberry Pi
Liaison série Microbit & Raspberry
Liaison série
Une liaison série nécessite l’utilisation de 2 fils (3 avec la masse) entre les 2 équipements.
- TX : borne de transmission du signal.
- RX : borne de réception du signal.
La liaison série va se faire au travers du câble USB.
Pré-requis sur le Raspberry Pi
Sur le Raspberry Pi, on installe python3 et pip3 .
sudo apt-get update && sudo apt-get install -y python3 python3-pip
Puis on télécharge/installe le module pyserial avec pip3.
Ce module pyserial permet de gérer une liaison série, mais dans le programme on écrira import serial
.
pip3 install --user pyserial
Microbit => Raspberry
Microbit utilise la fonction
Le baudrate de cette liaison est 115200.
Sur Microbit : le programme émetteur
Avec l’éditeur Mu, on stocke ce programme dans la Microbit.
En réalité Mu utilise aussi une liaison série pour communiquer avec la microbit.
# Pgm Microbit : l'émetteur de la liaison série
from microbit import *
while True:
print("1") # envoi sur la liaison série (port microUSB)
display.show("1") # affichage led, juste pour vérifier
sleep(1) # pause de 1 seconde
print("2")
display.show("2")
sleep(1)
Sur Raspberry Pi : le programme récepteur
Puis on connecte la Microbit à un port usb du Raspberry : l’alimentation usb lance donc immédiatement le programme « émetteur » de la Microbit.
Ainsi, une liaison série se crée avec Raspberry OS.
Pour trouver le n° de port série, on liste :
ls -l /dev/ttyA*
Ou bien ainsi :
ls -l /dev/serial/by-id
Puis on écrit le programme python nano recepteur.py
Pour ajuster le timeout, des explications sont fournies plus bas.
Pour trouver le port série en liaison avec la microbit : voir la fonction python plus bas.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
### Pgm Raspberry Pi : le récepteur de la liaison série ###
# On ajoute au path l'emplacement des modules python
# ce sera utile pour lancer ce programme dès le boot
import sys
sys.path.append('/home/pi/.local/lib/python3.7/site-packages')
sys.path.append('/home/pi/.local/bin')
import serial, time
# facultatif :
print(f"Receveur de liaison série python, version : {serial.VERSION}")
# initialisation de la liaison série
PORT = "/dev/ttyACM0" # remplacer par le n° port ou utiliser une fct de recherche
# On ouvre une liaison série sur le port "/dev/ttyACM0"
# 3 valeurs de timeout : None, 0, un flottant
s = serial.Serial(PORT, baudrate=115200, timeout=1)
time.sleep(0.1) # attendre 100ms que la connexion soit prête
s.reset_input_buffer() # vide le buffer des entrées (input buffer)
s.reset_output_buffer() # et le buffer des sorties (output buffer)
while True:
try:
# On lit une ligne encodée, enlève le '\n' puis décode en str.
data = s.readline().rstrip().decode('UTF-8')
print(data) # affichage dans REPL
except:
pass
s.close()
Enfin on lance le programme avec la ligne de commande bash :
sudo python3 recepteur.py
Ctrl+Z permet d’arrêter le programme Python sur le Raspberry Pi.
Pensez aussi à vider régulièrement les buffers (par exemple dans la boucle while
).
Note sur read()
et readline()
:
s.read()
lit 1 byte, donc 1 octet car, par défaut dansserial
, 1 byte = 8 bits.
Le type de la valeur retournée est bytes , il s’agit d’une chaine de caractère encodée. L’encodage peut être UTF-8, UTF-16, windows-1255 … Donc en décodant le bytes, on récupère un str.s.read(5)
lit 5 bytes (5 est une un exemple d’entier), donc 5 octets. Mais si un timeout a été mis alors la lecture peut se terminer prématurément et retourner moins de 5 octets. Type de la valeur retournée : bytes.s.readline()
lit au maximum une ligne, c’est-à-dire une chaîne se terminant par le marqueur de fin de ligne binaireb'\n'
. Le marqueurb'\n'
est inclus à la fin de la valeur retournée.
Il est conseillé de mettre un timeout pour ne pas bloquer le programme en cas d’absence deb'\n'
.s.readlines()
tente de lire « toutes » les lignes, mais comme la liaison est ouverte alors c’est confus. Pour clarifier, on met un timeout ets.readlines()
retourne la liste de lignes reçues, mais leb'\n'
n’est pas inclus.
Note sur le timeout :
Le timeout ne concerne que la lecture, donc read, readline, readlines :
timeout = None
est bloquant (valeur par défaut). L’instruction attend … attend … jusqu’à recevoir le nombre attendu d’octets, par exemple 5 octets pours.read(5)
timeout = 0
est non bloquant. Il retourne immédiatement 0 ou plusieurs octets (dans la limite du nombre d’octets attendus, par exemple 5 octets pours.read(5)
).timeout = 2.5
met une durée maximum de 2.5 secondes (2.5 est juste un exemple de flottant). Si le nombre d’octets a été reçu avant la fin du timeout alors le message est retourné, sinon il attend la fin du timeout et retourne les octets reçus jusque-là.
Note sur la durée de transmission :
Chaque caractère transmis occupe 10 bits (par défaut on compte 8 bits de data + 1 bit de début + 1 bit de fin) à une vitesse de 115 200 bits par seconde, donc 100 caractères prennent environ 9 ms.
Documentation sur la classe Serial
de pySerial : lien pdf
Trouver le port série en liaison avec la microbit :
import os
def port_microbit():
if os.path.isdir('/dev/serial/by-id'):
# liste_équipmt est une liste de str (le nom du fichier crée par l'OS)
liste_équipmt = os.listdir('/dev/serial/by-id')
print(f"La liste des équipmts série : {liste_équipmt}")
# on ne garde que les équipmts microbit
liste_microbit = [ équipmt for équipmt in liste_équipmt \
if 'usb-ARM_BBC_micro:bit' in équipmt]
print(f"La liste des microbits : {liste_microbit}")
# on suppose qu'on n'a branché qu'une microbit, donc la gagnante est [0]
# pour récupérer le nom raccourci du port, on suit les symlink
symlink = os.path.join('/dev/serial/by-id', liste_microbit[0])
from pathlib import Path
return str(Path(symlink).resolve())
else:
print('le répertoire n''a pas été trouvé')
return None
print(port_microbit()) # "/dev/ttyACM0"
Raspberry => Microbit
Sur Raspberry Pi : le programme émetteur
Sur le Raspberry, faire nano envoyeur.py
et copier/coller ceci :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
### Pgm Raspberry Pi : l'envoyeur de la liaison série ###
# On ajoute au path l'emplacement des modules python
# ce sera utile pour lancer ce programme dès le boot
import sys
sys.path.append('/home/pi/.local/lib/python3.7/site-packages')
sys.path.append('/home/pi/.local/bin')
import serial, time
# facultatif :
print(f"Receveur de liaison série python, version : {serial.VERSION}")
# initialisation de la liaison série
PORT = "/dev/ttyACM0" # remplacer par le n° port ou utiliser une fct de recherche
# On ouvre une liaison série sur le port "/dev/ttyACM0"
# 3 valeurs de timeout : None, 0, un flottant
s = serial.Serial(PORT, baudrate=115200, timeout=1)
time.sleep(0.1) # attendre 100ms que la connexion soit prête
#
s.reset_input_buffer() # vide le buffer des entrées (input buffer)
s.reset_output_buffer() # et le buffer des sorties (output buffer)
while True:
s.write("Hello World !".encode('UTF-8'))
print("message envoyé")
time.sleep(1) # attendre 1 seconde (module time)
Pensez aussi à vider régulièrement les buffers (par exemple dans la boucle while
).
Sur Microbit : le programme récepteur
Avec l’éditeur Mu, on stocke ce programme dans la Microbit (en réalité Mu utilise aussi une liaison série pour communiquer avec la microbit).
# Pgm Microbit : Récepteur de liaison série
from microbit import *
# Le module uart est à l'intérieur du module microbit
uart.init(baudrate=115200)
while True:
if uart.any(): # si de la data attend dans le buffer
data = str(uart.read(), 'UTF-8')
display.scroll(data)
sleep(1000) # 1 seconde (module microbit)
Note sur le timeout de Microbit
Le timeout de Microbit est non modifiable par Python, le timeout d’un caractère est calculé en milliseconde selon la formule : microbit_uart_timeout_char = 13000 / baudrate + 1
Pour un baudrate de 115200, on obtient 1.2 ms .
Taille du buffer de réception de la Microbit = 64 octets.
Note sur read et readline
Les fonctions read
et readline
de Microbit sont toutes NON BLOQUANTES :
read()
lit autant d’octets que possible … dans la limite du timeout et du buffer (64 octets).
La valeur retournée est de type bytes, on peut la convertir en str :txt = str(uart.read(), 'UTF-8')
Si le read_buffer est resté vide jusqu’au timeout alors elle renvoieNone
.
read(6)
lit 6 octets. Mais si le timeout a été atteint alors elle ne renvoie que les octets reçus jusque là.readline()
lit une séquence jusqu’à trouver le marqueur de fin de ligne. Elle retourne les bytes reçus y compris le marqueur de fin de ligne.
Si le timeout a été atteint alors elle renvoieNone
.
Lorsque le buffer est plein, les nouvelles données arrivantes sont ignorées, donc perdues.
Microbit <=> Raspberry
La liaison bidirectionnelle est tout à fait possible : il suffit d’assembler pour chaque équipement les 2 bouts de programme : envoyeur + récepteur.
Le programme suivant envoie un entier c d’un équipement à l’autre, puis dès que l’un d’eux reçoit un entier , il incrémente cet entier puis le renvoie à l’autre équipement.
C’est la microbit qui démarre ce ping-pong en envoyant 1 au Raspberry Pi.
Conséquence : La microbit ne reçoit que des entiers pairs et envoie des entiers impairs.
Et inversement pour le Raspberry Pi.
Sur Microbit : programme bidirectionnel
Au tout début, la microbit envoie l’entier c = 1
Puis elle reçoit en retour, une chaîne data qui contient un nouvel entier.
La microbit incrémente l’entier et le renvoie au Raspberry Pi.
# Pgm Microbit : liaison série bidirectionnelle
from microbit import *
uart.init(baudrate=115200)
# L'entier de départ : c'est la microbit qui commence le ping pong
c = 1
while True:
# Tant qu'on ne reçoit pas de réponse, on répète l'envoi
while not uart.any():
print(c) # envoi l'entier c vers RPi
sleep(1000) # 1 seconde (selon le module microbit)
# On lit la réponse reçue
data = str(uart.read(), 'UTF-8')
display.scroll(data) # on l'affiche sur les leds
c = int(data) + 1 # on incrémente l'entier reçu puis on fait une boucle d'envoi
Sur Raspberry Pi : programme bidirectionnel
Le Raspberry Pi reçoit une chaîne data qui contient un entier.
Le Raspberry Pi incrémente cet entier et le renvoie à la microbit.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
### Pgm Raspberry Pi : liaison série bidirectionnelle ###
# On ajoute au path l'emplacement des modules python
# ce sera utile pour lancer ce programme dès le boot
import sys
sys.path.append('/home/pi/.local/lib/python3.7/site-packages')
sys.path.append('/home/pi/.local/bin')
import os, serial, time
def port_microbit():
if os.path.isdir('/dev/serial/by-id'):
# liste_équipmt est une liste de str (le nom du fichier crée par l'OS)
liste_équipmt = os.listdir('/dev/serial/by-id')
# on ne garde que les équipmts microbit
liste_microbit = [ équipmt for équipmt in liste_équipmt \
if 'usb-ARM_BBC_micro:bit' in équipmt]
# on suppose qu'on n'a branché qu'une microbit, donc la gagnante est [0]
# pour récupérer le nom raccourci du port, on suit les symlink
symlink = os.path.join('/dev/serial/by-id', liste_microbit[0])
from pathlib import Path
return str(Path(symlink).resolve())
else:
print('le répertoire n''a pas été trouvé')
return None
# initialisation de la liaison série en mode BLOQUANT
port_BBC = port_microbit()
s = serial.Serial(port_BBC, baudrate=115200, timeout=None)
time.sleep(0.1) # attendre 100ms que la connexion soit prête
while True:
try:
data = s.readline().rstrip().decode('UTF-8') # on lit juste 1 ligne
s.reset_input_buffer() # vide le buffer des entrées
print(data) # affichage dans REPL
c = int(data) + 1 # on incrémente l'entier reçu
s.reset_output_buffer() # on vide le buffer des sorties
s.write(str(c).encode('UTF-8'))
print(f"message envoyé : {c}")
time.sleep(1) # attendre 1 seconde (module time)
except:
pass
s.close()
Les trames d’une liaison série
Une trame UART est constituée des bits suivants :
- un bit de début toujours à 0 : servant à la synchronisation du récepteur
- les données : leur taille est comprise entre 5 et 9 bits.
Les bits sont envoyés « à l’envers » : du LSB au MSB. - un bit de parité : paire ou impaire (optionnel)
- un bit de stop, toujours à 1. La durée du bit de stop varie entre 1, 1.5 et 2.
Le niveau logique de repos est le 1.
Installer un écran LCD 3.5″
Il y a 3 type de connexion d’écran :
- DSI : le plus cher, mais il laisse disponible les 2 ports : HDMI & GPIO.
- HDMI : plus abordable mais il faut ajouter un port usb pour l’alimenter.
- GPIO : le moins cher mais il monopolise le port GPIO. C’est celui présenté ci-dessous.
Caractéristiques
L’écran LCD est incompatible avec la Pi Camera
Les instructions du constructeur datent de 2016 sur Raspbian Wheezy.
Problème référencé en 2022 : https://github.com/goodtft/LCD-show/issues/313
Nom : 3.5inch RPi LCD (A)
- Compatible avec Raspberry Pi A, B, A+, B+, 2B, 3B, 3B+, 4B
- Resolution : 320×480
- Ecran LCD de type TFT
- Ecran tactile de type résistif
- Controller tactile : XPT2046
- Interface : SPI
Wiki du constructeur : https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)
Fiche produit du constructeur : https://www.waveshare.com/product/3.5inch-rpi-lcd-a.htm
Connecter l’écran
Connecter l’écran sur les 26 broches du GPIO (qui lui, possède 40 broches), comme ci-dessous :
Ensuite, on télécharge et installe les drivers dans Raspberry Pi OS.
Télécharger les pilotes (drivers)
Les pilotes sont disponibles dans un dépot Github : https://github.com/waveshare/LCD-show
Il s’agit d’un dossier nommé LCD-show
à télécharger (cloner) avec le logiciel git.
Installons le logiciel git :
sudo apt-get install git
Et valider l’installation en tapant : Y
Une fois git installé, on va créer par précaution un répertoire temporaire dans notre home ~
pour télécharger/cloner les pilotes LCD :
cd ~
mkdir temp
cd temp
git clone https://github.com/waveshare/LCD-show.git
Vérifions le téléchargement avec un « affichage long » pour avoir tous les détails :
cd LCD-show
ls -l
Vérifier que le fichier LCD35-show
est bien exécutable.
Si besoin, on le rend exécutable en tapant : chmod +x LCD35-show
Puis on lance l’installation depuis le répertoire courant, qui est donc le répertoire .
Attention, la commande change selon le type d’OS :
- Sur Raspberry Pi OS lite (c’est notre version), on exécute :
sudo ./LCD35-show lite
- Sur Raspberry Pi OS, on exécute :
sudo ./LCD35-show
Et, valider l’installation en tapant : Y
Remarque : Normalement une exécution de fichier se fait depuis un répertoire, donc en tapant dossier/fichier
Cependant, si on se situe déjà dans le répertoire alors c’est le répertoire courant qu’on spécifie ./fichier
A la fin de l’installation, le Raspberry redémarre, donc la connexion SSH se coupe.
… Attendre 4 min …
Puis se reconnecter.
Supprimer le répertoire temporaire :
On peut dorénavant supprimer le répertoire temp :
- il faut d’abord se déplacer à l’extérieur du répertoire avec
cd
- puis on fait une suppression récursive
rm -r
de ce répertoire et de tous les répertoires qu’il contient.
cd ~
rm -r ~/temp
En cas de problème : tester ces pilotes https://github.com/goodtft/LCD-show
et garder exactement les mêmes lignes de commande.
Installer Raspberry Pi OS
On va installer l’OS sur une carte micro-SD de capacité 2 Go ou plus.
A partir d’un ordi Windows
Télécharger une image de Raspberry Pi OS
Une version lite est suffisante :
- Debian 11, nommée Bullseye, /!\ Attention, la PiCamera est incompatible avec Bulleye /!\ :
Choisir la date la plus récente- Version lite : le fichier zip (482 Mo) sur https://downloads.raspberrypi.org/raspios_lite_armhf/images/
- >> Debian 10, nommée Buster <<
- Version lite : le fichier ZIP (444 Mo) sur https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/
Puis il faut décompresser l’archive ZIP téléchargée, on obtient alors un fichier *.img
Télécharger Rufus
Pour copier/installer le fichier image sur une carte micro-SD, il faut installer un logiciel de type « disk imager ».
Par exemple : Rufus (Windows) : https://rufus.ie/fr/
Copier l’image sur la carte micro-SD
Insérer la micro-SD dans l’adaptateur … puis dans l’ordinateur.
Ouvrir Rufus :
- Périphérique : sélectionner la carte SD.
- Type de démarrage : laisser par défaut (image disque ou ISO)
- SELECTION : Choisir le fichier précédemment téléchargé puis décompressé
*.img
A la fin du flash, la carte contient alors 2 partitions :
- une partition Linux
- une partition boot (de type Fat32) qui pourra être lue par Linux et Windows.
Ajouter un accès SSH
Par défaut, l’accès SSH est désactivé.
Pour l’activer, il faut simplement ajouter un nouveau fichier vide nommé ssh
/!\ Il s’agit d’un fichier sans extension.
Ejecter la micro-SD puis la ré-insérer dans l’ordinateur.
Windows va proposer de formater la partition Linux (plusieurs fois) : il faut refuser le formatage.
Dans l’explorateur, ouvrir la micro-SD (c’est la partition boot) et ajouter un nouveau fichier texte.
Vérifier que l’affichage des extensions de fichiers est activé : dans l’explorateur de fichiers > onglet Affichage.
Renommer le fichier « Nouveau document texte.txt » en « ssh »
A partir d’une distribution Linux (en ligne de commande)
1ère étape : On se connecte en tant que root.
On met à jour la distribution de l’OS :
apt-get update && apt-get -y dist-upgrade
2e étape : On insère la carte SD dans l’ordi linux (grâce à un adaptateur USB-microSD).
On va récupérer le nom du device (qui sera sûrement /dev/sda
) et ses partitions (ex : /dev/sda1
et /dev/sda2
) :
fdisk -l
3ème étape : On fait vérifier l’intégrité de chaque partition et accepte les réparations (fix) le cas échéant :
fsck /dev/sda1
# idem avec /dev/sda2
4ème étape : Pour préparer la carte SD au flash, on va la repartitionner et reformater.
Il faut d’abord s’assurer que la carte ne soit pas « montée » (c’est-à-dire un disque en lecture dans l’OS).
On affiche la liste des disques montés :
df -h
Si jamais la carte SD est montée alors il faut démonter toutes les partitions : umount /dev/sda1
et umount /dev/sda2
5ème étape : On va supprimer toutes les partitions avec l’utilitaire fdisk
fdisk /dev/sda
Taper d
(pour delete).
S’il y a plusieurs partitions, alors fdisk va demander le n° de la partition à supprimer.
Recommencer pour supprimer chaque partition, en tapant d
.
A la fin, taper w
(pour écrire et sauvegarder).
6ème étape : On va recréer une partition de type ms-dos et la formater avec l’outils parted.
parted /dev/sda
Puis pour les commandes, taper :
mklabel msdos
(pour créer une table) puisYes
pour validerprint
pour voir la taille du disque, ici c’est 8053 MBmkpart primary ext4 1MB 8053MB
pour créer une partition de 8 Go, il faut adapter selon la taille affichée avecprint
quit
8ème étape : On télécharge une image de Raspberry Pi OS.
Prenons une 32bit-BULLSEYE (debian 11) en lite (sans desktop).
cd /home/pi
wget "https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2022-09-26/2022-09-22-raspios-bullseye-armhf-lite.img.xz"
Vérifions avec la commande ls
:
9ème étape : On décompresse l’image téléchargée *.xz
(c’est très long !!) puis on flashe la carte CD avec cette image (c’est très long aussi).
xz --decompress *.xz
dd bs=1M if=2022-09-22-raspios-bullseye-armhf-lite.img of=/dev/sda status=progress conv=fsync
Note : bs
(block size) = 1M
est une vitesse lente car la vitesse normale est plutôt 4M.
10ème étape : Pour placer les fichiers ssh.txt
et userconf.txt
, on a besoin de monter les 2 partitions de la carte SD (qui ont été créées par le flashage).
On va créer 2 points de montage en /mnt/boot
et /mnt/raspbian
(ou 2 autres répertoires vides).
mkdir /mnt/boot
mkdir /mnt/raspbian
mount /dev/sda1 /mnt/boot
mount /dev/sda2 /mnt/raspbian
On crée un fichier vide ssh.txt
dans la partition boot
de la carte SD :
touch /mnt/boot/ssh.txt
On ajoute une marge à gauche et en bas (positive) pour éviter les débordement hors de l’écran, à la fin du fichier config.txt
:
cat >> /mnt/boot/config.txt <<'EOF'
overscan_left=50
overscan_bottom=50
EOF
On chiffre le mot de passe « nsirennes » :
echo 'nsirennes' | openssl passwd -6 -stdin
Puis on affecte ce mot de passe à l’utilisateur pi
dans un fichier userconf.txt
sous la forme pi:mot_de_passe_hashé
:
cat > /mnt/boot/userconf.txt <<'PWDEOF'
pi:$6$kbPS1eG8Vnvkw4q1$s/e6kzL2S0k9xaVvIYt27HtOnL8lENC8QxiR.h/82tFJoX/0O18lW4VJIq9wLw9oCiXRMbXonUYVvR.3V7UxI1
PWDEOF
Note : à chaque fois qu’on lance un hashage, on obtient un mot de passe chiffré différent car le programme utilise une petite dose d’aléatoire (appelé « sel » ou « salt »).
11ème étape : on démonte les partitions :
cd ~
umount /mnt/boot
umount /mnt/raspbian
rm -rf /mnt/boot
rm -rf /mnt/raspbian
Configurer l’OS
- Ejecter la carte micro-SD de l’ordinateur et l’insérer dans le Raspberry Pi
- Relier le Raspberry Pi avec un câble Ethernet
- Alimenter le Raspberry Pi avec un câble micro-usb.
Attendre 3 minutes (le temps du démarrage).
Ouvrir Putty pour démarrer une connexion SSH (port 22) :
Hostname : raspberrypi
Port : 22
Le login de connexion est :
Login : pi Mot de passe : raspberry
Puis pour autoriser un accès root, on change son mot de passe :
sudo passwd root
et choisir un mot de passe : ******
Puis se connecter en tant que superutilisateur root :
su root
On change le mot de passe de l’utilisateur pi (pour éviter les message d’alerte de mot de passe inchangé) :
passwd pi
et choisir un mot de passe : ******
Puis copier/coller ceci afin permettre :
- la connexion SSH en tant que root
- changer le clavier en AZERTY
- ajouter la coloration syntaxique dans Bash.
Rappel : Dans un terminal linux, il faut faire clic droit pour coller.
mkdir -p /root/dos
touch /root/hello.c
touch /root/hello.js
sed -i "s/#PermitRootLogin .*/PermitRootLogin yes/1" /etc/ssh/sshd_config
systemctl restart ssh
rm -f /etc/localtime
echo "Europe/Paris" >/etc/timezone
dpkg-reconfigure -f noninteractive tzdata
cat >/etc/default/keyboard <<'KBEOF'
XKBMODEL="pc105"
XKBLAYOUT="fr"
XKBVARIANT=""
XKBOPTIONS=""
KBEOF
dpkg-reconfigure -f noninteractive keyboard-configuration
cat >> ~/.bashrc <<"EOF"
alias ls='ls --color=auto'
force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
EOF
Changer le hostname
Si on veut mettre plusieurs Raspberry Pi sur le réseau alors il faut leur donner des HostName différents.
Voici comment renommer le hostname en raspberrypi0
:
sed -i "s/\(^127\.0\.1\.1.*raspberrypi.*$\)/127\.0\.1\.1 raspberrypi0/1" /etc/hosts
sed -i "s/raspberrypi/raspberrypi0/1" /etc/hostname
Mettre à jour le Firmware du Raspberry
sudo apt-get install rpi-update
sudo rpi-update
En cas de problème de partition (carte SD)
Si Rufus refuse la carte SD alors :
- touche Windows + R et taper
diskpart
- taper :
list volume
- si la carte SD est le volume 3 alors taper :
select volume 3
- pour supprimer taper :
delete volume
Puis on fait un nettoyage :
- taper :
list disk
- si la carte SD est le disque 1 alors taper :
select disk 1
- taper :
clean
- pour quitter, taper :
exit
En cas de problème : carte SD & explorateur
Dans la barre de recherche Windows : taper partition de disque
Puis il faut lui assigner une lettre de disque, par exemple : D
Pour tester la réelle capacité de la SDcard
Beaucoup de carte SD sont « fake » :
- Elles ont une capacité moindre.
Mais leur firmware mentionne le contraire à l’hôte : par conséquent, les données débordantes sont silencieusement ignorées. - Elles ont une vitesse lente de lecture/écriture
Sur Windows, le logiciel H2testw teste ces 2 défaillances :
- téléchargement direct de H2testw 1.4
- tutoriel : https://quick-tutoriel.com/verifier-la-fiabilite-et-la-capacite-dune-cle-usb/
Sur Linux, c’est F3.
Sur Mac, c’est F3X ou F3XSwift.