Catégorie : architecture matérielle & OS

Raspberry Pi

Raspberry Pi

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 Python picamera

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 :

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 de daemon off
  • width 640 au lieu de width 320
  • height 480 au lieu de height 240
  • stream_localhost off au lieu de stream_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 print pour envoyer un signal sur la liaison série.
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 dans serial, 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 binaire b'\n'. Le marqueur b'\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 de b'\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 et s.readlines() retourne la liste de lignes reçues, mais le b'\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 pour s.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 pour s.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 renvoie None.
  • 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 renvoie None.

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 de 10 bits (1 bit début + 8 bits data + 1 bit stop)

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 :

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 &amp;&amp; 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) puis Yes pour valider
  • print pour voir la taille du disque, ici c’est 8053 MB
  • mkpart primary ext4 1MB 8053MB pour créer une partition de 8 Go, il faut adapter selon la taille affichée avec print
  • 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 :

Sur Linux, c’est F3.

Sur Mac, c’est F3X ou F3XSwift.

TP Réseaux

TP 1

durée = 1h30

En situation réelle

Configuration réseau de l’équipement

Sur Windows

Taper :

  • cmd pour ouvrir l’invite de commande.
  • ipconfig pour voir l’adresse MAC et l’adresse IP de votre ordinateur
    ou ipconfig/all pour davantage d’informations.

Sur Linux

  • ifconfig ou ip a dans une console Linux pour voir l’adresse MAC et l’adresse IP.

Tester la connexion vers un site :

Sur Windows

  • ping www.google.fr pour voir si le serveur est joignable.
  • tracert www.google.fr pour connaître le chemin pour atteindre un site :  

Sur Linux

  • ping www.google.fr puis Ctrl+C pour arrêter les pings.
  • traceroute www.ubuntu-fr.org pour voire la listes des routeurs traversés jusqu’au serveur d’Ubuntu.

TP Filius (réseau virtuel)

Le logiciel Filius contient 3 modes :

  • le mode construction/conception : l’icone marteau 🔨
  • le mode lecture (exécution) : la flèche verte
  • le crayon ✎ sert à annoter un schéma

1er montage

  • Réaliser le montage ci-dessous.
  • Ajouter l’adresse IP et le masque de réseau de chacun.
  • Passer en mode lecture (flèche verte).
  • Ajouter un logiciel de ligne de commande dans chaque équipement (en double cliquant sur le logiciel, puis « appliquer les modifications »)
  • Vérifier que tout fonctionne avec des pings entre les différents équipements.

2ème montage

Télécharger ce fichier : le montage filius

1°) Chez BOB : ping vers Alice :

  • Se mettre en mode lecture (flèche verte).
  • clic droit sur BOB, et afficher les échanges … en petite fenêtre (c’est juste pour voir qu’elle se remplit progressivement)
  • clic sur l’invite de commande
  • Lancer une salve de pings vers Anna : ping 192.168.2.1  et  observer le passage de paquets

Observations :

  • Sur le 1er paquet = ARP : BOB demande au routeur son adresse MAC avec l’adresse MAC de broadcast FFFF..FF
    Le protocole ARP permet d’associer une adresse IP à une adresse MAC.
  • 2ème paquet : Cette fois, on remarque que les 2 @MAC sont renseignées … comment le routeur a-t-il fait pour connaitre @MAC de BOB ?
  • Observer les alternances ping et pong.
  • Quelle est @IP destinataire ? C’est @IP de Alice (ok c’est cohérent)
    Qu’en est-il de l’@MAC destinataire, est-ce Alice ? ….
  • Puis on efface tous ces échanges : clic droit dans la table (zone verte) ou mieux encore recharger le fichier téléchargé.

2°) Chez BOB : navigateur internet

  • Se mettre en mode lecture (flèche verte).
  • clic droit sur BOB, et afficher les échanges … en petite fenêtre
  • clic sur le navigateur
  • Pour visiter le site web du serveur web, taper :  192.168.3.1  et  observer le passage de paquets

Observations :

  • 1er paquet = SYN : demande une connection TCP au serveur
    2e paquet = SYN/ACK = synchronyze   and   acknowledgement : la poignée de main en 3 phases
  • 4e paquet : BOB fait une demande GET de la page web
  • 6e paquet : BOB reçoit le fichier HTML (un fichier texte)
  • 8° paquet : BOB demande l’image et là ça va prendre plusieurs paquets !!
  • Puis ligne 22 : BOB demande à mettre Fin à la connexion TCP : la poignée de main en 4 phases.

3°) En changeant d’image :

Sur le serveur WEB :

  • Ouvrir son éditeur de texte et ouvrir le fichier  index.html
  • Modifier le nom de l’image par :    tux-ski.jpg
  • Sauvegarder

Chez BOB :

  • Actualiser la page WEB et observer

Recommencer avec une erreur dans le nom de l’image !

TP 2

durée = presque 2h.

Table ARP

1ère étape :

  • Sur Filius, réaliser le montage avec 2 ordinateurs. Dans les paramètres de chaque ordinateur, cocher la case « Utiliser l’adresse IP comme nom« .
  • Passer en mode simulation . Par un double-clic sur la première machine (192.168.1.1), ouvrir l’installateur de logiciels.
  • Installer la ligne de commande en la faisant passer à gauche avec 
  • Ouvrir la ligne de commande (double-clic) et saisir l’instruction ipconfig
  • Saisir la commande arp à l’invite de commande.
  • Enregistrer ce montage dans un fichier (avant de faire des échanges).

2ème étape :

  • Effectuer un ping vers l’autre ordinateur, ex : ping 192.168.1.2
  • Saisir à nouveau la commande arp à l’invite de commande … Qu’est ce qui a changé depuis le ping ?
  • Faire un clic-droit sur la machine 192.168.1.1 et afficher les échanges de données. Repérer l’échange avec le protocole ARP .

Table CAM/SAT d’un switch

  • Reprendre le fichier précédent (vierge de tout échange). Supprimer le câble (clic droit) et ajouter un switch (commutateur) et un 3e ordinateur.
  • Par un simple clic sur le switch, afficher la table SAT/CAM du switch (vérifier qu’elle est vide).

1er ping :

  • Installer la ligne de commande sur 192.168.1.1 et lancer un ping vers le 2e ordinateur .1.2.
  • Afficher à nouveau la table du switch. Qu’est ce qui a changé depuis le ping ?
  • Afficher les échanges du 3e ordinateur … Pourquoi a-t-il reçu une requête ARP ? A-t-il répondu à cette requête ARP ?

2ème ping :

  • Toujours depuis la ligne de commande de 192.168.1.1 et lancer un ping vers le 3e ordinateur .1.3 cette fois.
  • Afficher à nouveau la table du switch. S’est-elle encore enrichie ?
  • Afficher les échanges de ce 3e ordinateur … Pourquoi a-t-il encore reçu une nouvelle requête ARP ? A-t-il répondu cette fois ?

Routeur et passerelle

Réaliser ce nouveau montage utilisant un routeur à 2 interfaces.
Bien noter que ce montage contient 2 réseaux : 192.168.0.0/24 et 192.168.2.0/24.

1ère partie / sans configurer le routeur :

  • En mode simulation (flèche verte), sur la machine 192.168.0.1, tester les connexions vers les autres machines avec la commande ping.
  • Quelles machines ne peuvent pas être atteintes par .0.1 ?

2ème partie / en configurant le routeur :

  • En mode construction 🔨 (marteau), donner une adresse IP (du sous-réseau) à chaque interface du routeur. Par exemple :   .254
  • tester les connexions vers les machines de l’autre réseau avec la commande ping. Le problème est-il résolu ?

3ème partie / En configurant aussi les ordinateurs :

  • En mode construction 🔨 (marteau), renseigner la passerelle (gateway) de chaque ordinateur.
    Attention, chaque réseau possède sa propre passerelle !
  • Le problème est-il enfin résolu ?
  • Effectuer un traceroute (ligne de commande) de la machine 192.168.1.1 vers le portable 192.168.2.1.
  • Enregistrer ce fichier : on en a besoin pour la partie suivante.

Serveur DNS

Reprendre le montage précédent :

  • En mode conception 🔨 (marteau), double-cliquer sur le routeur puis Configurer > Gérer les connections.
  • En cliquant sur +, ajouter une troisième interface locale. Fermer la fenêtre et se rendre dans l’onglet correspondant pour lui attribuer l’adresse IP  .254
  • Ajouter un nouvel ordinateur et le connecter à la troisième interface fraîchement créée du routeur. Attribuer l’adresse IP 192.168.1.1 à cet ordinateur (futur serveur) et n’oubliez pas de renseigner l’adresse de la passerelle.
  • En mode simulation (flèche verte), ajouter un serveur DNS à ce nouvel ordinateur. Configurer ce serveur DNS en ajoutant, dans l’onglet Adresse(A), le nom de domaine www.nsi.fr à l’adresse IP 192.168.2.2 (l’autre ordinateur « tour » tout à droite).
  • Démarrer le serveur DNS : clic sur « Démarrer« .
  • En mode construction 🔨 (marteau), configurer le champs DNS de tous les ordinateurs portables en précisant l’adresse IP du serveur DNS crée.
  • En mode simulation , effacer les échanges.
  • Effectuer un ping www.nsi.fr à partir de différentes machines du réseau (sur lesquelles on aura installé la ligne de commande).
  • Afficher les échanges … Remarquer les 2 échanges UDP (aller-retour) vers le serveur DNS 192.168.1.1 qui contiennent les 4 couches :
    • liaison de données : @ MAC
    • réseau : @ IP
    • transport : UDP, port 53, le TTL (time to live) en secondes.
    • application : requête DNS de www.nsi.fr

Serveur Web

On veut maintenant héberger nos pages web sur un serveur de notre réseau. Ce serveur devra être accessible par toutes nos machines, via l’URL www.nsi.fr. Nous choisissons donc la machine 192.168.2.2 (à droite) comme serveur web.

Sur le serveur 192.168.2.2 (à droite) :

  • Enregistrer le précédent réseau, comprenant le serveur DNS, sous un autre nom (ex : ServeurWeb.fls)
  • Passer en mode simulation (flèche verte)
  • Installer un logiciel de serveur Web et un éditeur de texte sur la machine 192.168.2.2 (à droite).
  • Démarrer le serveur web.
  • A l’aide de l’éditeur de texte (Menu Fichier / Ouvrir), ouvrir le fichier index.html qui est déjà dans le dossier webserver. Le modifier à votre guise et enregistrer.

Sur le client 192.168.0.1 :

  • Installer un client Web (navigateur web) sur la machine 192.168.0.1.
  • Démarrer le navigateur et saisir l’URL www.nsi.fr dans la barre d’adresse, pour envoyer une requête HTTP au serveur Web. La page d’accueil du serveur devrait s’afficher.
  • Activer l’affichage des données sur la machine 192.168.1.1 avec un clic-droit.
  • Au besoin, relancer la requête HTTP précédente (www.nsi.fr) à l’aide du navigateur puis analyser l’échange de données.

http vs https avec Wireshark

Pour voir si une transmission est chiffrée ou non, il faut :

  • un logiciel de capture de trames : Wireshark.
  • et plutôt Firefox (car Chrome refuse parfois les connections non sécurisées).

Voici 2 pages de connexion, l’une est sécurisée (https) et l’autre ne l’est pas (http) :

En http, les mots de passe circulent en clair :


En https, le mot de passe est chiffré :

BBC micro:bit

BBC micro:bit

Editeur Micro-Python :

Présentation

Processeur : PU ARM® Cortex ™ M0 32 bits
Fréquence de l’horloge de processeur : 16 Mhz
RAM : 16 Ko
Courant des broches : 3,3 V
6 broches analogiques + 19 broches digitales

Entrée / Sortie

Sortie : écrire sur une broche

Digitale

pin2.write_digital(1)    impose 3V sur la broche 2 (en mode sortie).
pin2.write_digital(0)    abaisse la tension à 0V

~Analogique

pin2.write_analog(1015)   met une tension sur la broche 2 proportionnelle à la valeur 0-1023 (en mode sortie).

Entrée : mesure le signal sur une broche

Digitale

pin2.read_digital()    renvoie 0 ou 1 selon la tension mesurée sur la broche 2.
NB: read_digital() met automatiquement la broche en mode INPUT.


pin2.get_mode()    renvoie 'read_digital' ou 'write_digital' pour savoir si la broche est configurée en entrée ou sortie.


import machine
machine.time_pulse_us()
  attend que la broche passe à 1 puis envoie la durée en µs pendant laquelle la broche reste à 1.

from machine import time_pulse_us

# On met pin2 en mode INPUT    
pin2.read_digital()

# time_pulse_us attend que la broche passe à 1 puis
# renvoie la durée en µs pendant laquelle elle reste à 1
duree = time_pulse_us(pin2, 1)

~Analogique

pin2.read_analog()   renvoie une valeur de 0-1023 proportionnelle à la tension mesurée sur la broche 2.

x = pin2.read_analog()
print((x,)) # tuple 1 valeur

Attendre (delay) – pause

  • sleep(10)    pour attendre 10ms
  • import utime
    utime.sleep_us(10)
      pour mettre en pause pendant 10µs
  • running_time()   donne le temps écoulé depuis de démarrage de la carte en ms.

Affichage : 25 leds

  • display.clear()   efface tout

  • display.show("Hi!")     affiche un texte (lettre par lettre)
    display.show(Image.ARROW_W)     ou une image.
  • display.scroll("bonjour")   affiche un texte, en le faisant défiler progressivement.
  • display.set_pixel(x, y, n)   allume le pixel de coordonnée (x,y) d’une intensité n (de 1-9).
    NB : x et y sont dans 0-4. Donc le pixel central est (2,2).

dir(Image) tapé dans REPL : permet d’obtenir la liste des images


Pour utiliser les broches P3, P4, P6, P7, P9, P10 pour d’autres capteurs/actionneurs, il faut désactiver les leds :

  • display.off()   pour désactiver l’affichage
  • display.on()    pour réactiver les leds

display.is_on() et display.is_off() permettent de connaître l’état de l’écran.

Boutons A et B

from microbit import *
while True:
    if button_a.was_pressed():
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD)
    sleep(500)
  • was_pressed()   cette méthode renvoie True si le bouton a été actionné pendant que le programme était occupé à une autre tâche. 
  • is_pressed()     renvoie True si le bouton est actionné à l’instant où cette méthode est invoquée.
  • get_presses()   renvoie le nombre de pressions effectuées. Puis dès que cette méthode a été invoquée, le compteur est remis à 0.

Radio

La radio consomme de l’énergie et de la mémoire !!!

  • radio.on()   et  radio.off()     régulent l’usage de la radio

  • radio.config(channel=21)     règle la liaison radio sur un canal 0-83
  • radio.config(power=7)     règle la force du signal 0-7. La force 7 peut atteindre une distance de 70m.

  • radio.send("bonjour")     envoie un message

  • msg_recu = radio.receive()     stocke le message reçu dans la variable … ou None si rien n’a été reçu.

  • radio.receive_full()     renvoie un tuple (message, force du signal, timestamp) où :
    • la force du signal varie de 0 (fort) à 255 (faible).
    • Le timestamp est le résultat en microsecondes de time.ticks_us() .
  • radio.config(queue=4)     règle la file d’attente à 4 messages (par défaut c’est 3).

Coupler 2 cartes :
Il y a plusieurs possibilités :
* radio.config(channel=21)    pour choisir une fréquence
* radio.config(address=0x75626974)     une adresse fictive codée sur 4 octets.
* radio.config(group=7)     un groupe fictif entre 0-255. Seules les cartes ayant la même adresse et le même groupe communiqueront. L’adresse représente un immeuble, et le groupe l’appartement.

from microbit import *
import radio

radio.on()
radio.config(channel=19)  # Choisir un n° de channel (0-83)
radio.config(power=7)     # Règle la force du signal

message = "Hello NSI !"

# on appuiera sur Bouton A pour arrêter le programme
while not button_a.was_pressed():
    # on appuie sur bouton B pour envoyer le message
    if button_b.was_pressed():
        radio.send(message)
    msg_recu = radio.receive()
    if msg_recu is not None:
        display.show(msg_recu)
        print(msg_recu)
    sleep(500)
radio.off()

NB : L’instruction radio.receive() est non bloquante : elle n’attend pas la réception d’un message.
Si aucun message n’a été reçu au moment de l’appel à cette méthode, elle renvoie la valeur None.
Pour qu’elle soit bloquante : voir l’exemple de code ci-dessous.

Pour attendre la réception d’un message, c’est-à-dire bloquer le radio.receive() :

msg_recu = None
while msg_recu is None:
    msg_recu = radio.receive()
    sleep(500)

Gestion de la queue :
* Si la queue est pleine, les messages suivants seront ignorés (et perdus).
* Lire un message l’enlève de la queue.
* L’ordre de lecture est FIFO « First In First Out » : le premier message arrivé est le premier lu.

Pour plus d’information : https://microbit-micropython.readthedocs.io/en/latest/radio.html

Bus I2C et SPI

Le bus I2C (broches 19 et 20) permet de brancher plusieurs périphériques partageant le même bus de communication.
Ce bus est, par ailleurs, utilisé par Micro:bit pour le Magnétomètre et l’accéléromètre, il est possible d’ajouter d’autres senseurs I2C.

Le bus SPI, apprécié pour sa rapidité, est également disponible sur la Micro:bit.

Capteurs internes

Température

temperature()     affiche la température mesurée par le capteur interne de la micro:bit.

from microbit import *
while True:
    temp = temperature()
    print((temp,))
    sleep(1000)

Accéléromètre (gravité)

définition

On peut afficher les effets de la gravité terrestre sur l’accéléromètre de la micro:bit.
Elle est mesurée en milli-g où 1g est la force de la gravité de la Terre.

  • x = transversal
  • y = longitudinal
  • z = vertical
les 3 axes de l'accéléromètre de micro:bit

Si la micro:bit repose à plat sur une table :

Bleu X = Vert Y = 0

Orange Z = -1000 car la gravité s’exerce uniquement sur Z.
En retournant la micro:bit : Orange Z = +1000

exemple

from microbit import *

xSeulmt = True
while True:
    if button_a.was_pressed():
        xSeulmt = not xSeulmt
    if xSeulmt:
        print( (accelerometer.get_x(), ) )
    else:
        print( accelerometer.get_values() )
    sleep(100) # on attend 100 ms
  • accelerometer.get_x()   renvoie la mesure transversale.
  • accelerometer.get_y()   renvoie la mesure longitudinale.
  • accelerometer.get_z()   renvoie la mesure verticale.
  • accelerometer.get_values()   renvoie le tuple.
from microbit import *
while True:
    accX = accelerometer.get_x()
    if accX > 40:
        display.show(">")
    elif accY < -40:
        display.show("<")
    else:
        display.show("-")

détection de gestes

La microbit peut également détecter des gestes tels que « secouer » la carte.
Les gestes sont : up, down, left, right, face up, face down, freefall, 3g, 6g, 8g, shake
Comme pour les boutons, les gestes sont accumulés dans une pile.

  • accelerometer.was_gesture("shake")   renvoie True si une secousse a eu lieu.
  • accelerometer.is_gesture("shake")   renvoie True si la carte est secouée au moment où la méthode est invoquée.
  • accelerometer.get_gestures()   renvoie la pile de gestes

Boussole (compass)

/!\ Le compas doit toujours être calibré au préalable.

from microbit import *

compass.calibrate()
while True:
    angle = compass.heading()
    display.scroll(str(angle))
  • compass.calibrate()   pour calibrer le compas.
  • compass.is_calibrated()   renvoie True si le compas est calibré.
  • compass.heading()   donne la mesure entre 0 et 360.

Puis utiliser les images pré-définies pour indiquer le nord par une flèche : Image.ARROW_N, Image.ARROW_NE, Image.ARROW_E, Image.ARROW_SE, Image.ARROW_S, Image.ARROW_SW, Image.ARROW_W, Image.ARROW_NW.

from microbit import *
if not compass.is_calibrated():
    compass.calibrate()
while True:
    aiguille = ((15 - compass.heading()) // 30) % 12
    display.show(Image.ALL_CLOCKS[aiguille])

Magnétisme (compass)

from microbit import *

while True:
    sleep(100)
    print((compass.get_field_strength(),)) # tuple 1 valeur

compass.get_field_strength()  renvoie la force du champ magnétique (détecte la présence d’un aimant).

compass.get_x() et compass.get_y() et compass.get_z()   décompose la force selon les axes x, y et z

Son – Musique

Le module music

Sur la plupart des cartes shield incluant un module sonore, la broche est pin0 .

interrupteur du mode buzzer sur IObit_v2
l’interrupteur du mode buzzer sur IObit_v2

Dans le module music :

  • music.pitch(fréquence_en_Hz, durée_en_ms, broche)   génère un son de fréquence maximale 10 000 Hz (un entier).
    • La limitation de durée est facultative, par défaut elle est illimitée -1 .
    • La broche est facultative, par défaut c’est pin0 .
    • Ajouter l’argument wait=False pour que l’instruction ne bloque pas le programme.
  • music.play(music.BIRTHDAY)   joue la mélodie.
    Ajouter l’argument wait=False pour que l’instruction ne bloque pas le programme.
    Ajouter l’argument loop=True pour que la musique soit jouée en boucle.
  • music.stop(broche)   stoppe le son sur la broche.
    La broche est facultative, par défaut c’est pin0 .
from microbit import *
import music

for i in range (1,11):
    music.pitch(100*i, 2000, pin0) # i*100 Hz pendant 2 secondes
    print('Un son de fréquence',i*100,'Hz pendant 2 secondes')
from microbit import *
import music
while True:
    music.pitch(440,1000)
    sleep(1000)
    music.pitch(660,500)
    sleep(1000)
    music.pitch(880,750)
    sleep(1000)
    music.stop()
    sleep(1000)

La liste des mélodies :

dir(music) tapé dans REPL :

music.DADADADUM, music.ENTERTAINER, music.PRELUDE, music.ODE, music.NYAN, music.RINGTONE, music.FUNK, music.BLUES, music.BIRTHDAY, music.WEDDING, music.FUNERAL, music.PUNCHLINE, music.PYTHON, music.BADDY, music.CHASE, music.BA_DING, music.WAWAWAWAA, music.JUMP_UP, music.JUMP_DOWN, music.POWER_UP, music.POWER_DOWN

Exercice : Placer toutes ces mélodies dans une liste, puis écrire un code qui joue une musique aléatoire à chaque fois qu’on appuie sur le bouton A.
On pourra utiliser la fonction random.choice(tab) qui renvoie un élément aléatoire de la liste tab (importer le module random).

Mélodies

durée = 35 min

Voici quelques mélodies qu’on souhaiterait faire jouer :

melodie1 = "do - do - do - re - mi - re - do - mi - re - re - do"

melodie2 = "mi mi mi mi mi mi mi sol do re mi fa fa fa mi mi mi re re re mi re sol mi mi mi mi mi mi mi sol do re mi"

1ère étape : Choisir une de ces mélodies et la transformer en liste de notes ["do", "do", "do", "ré"] en utilisant la méthode split. Voici un exemple d’utilisation de split :

### Transforme la chaine en liste selon le séparateur "; " (avec l'espace)
liste = "thé; café; jus d'orange; eau gazeuse".split("; ")
# le résultat est la liste de str :
# liste = ['thé', 'café', "jus d'orange", 'eau gazeuse']

### Idem selon le séparateur espace
donnée = "13 -5 8 12.1"
liste = donnée.split(" ")
# attention, le résultat est une liste de str :
# liste = ['13', '-5', '8', '12.1']

2ème étape : Choisir une octave et créer un dictionnaire où les clés sont les 7 notes de cette octave et les valeurs sont les fréquences :

NOTE   1ère octave    2e octave  
do131262
147294
mi165330
fa175349
sol196392
la220440
si247494
(do)(262)(523)

3ème étape : Au moyen d’une boucle, parcourir la liste des notes de la mélodie et grâce au dictionnaire jouer la fréquence de la note avec l’instruction music.pitch(fréquence_en_Hz, durée_en_ms) .

4ème étape : Voici une 3e mélodie avec un peu de rythme cette fois : en multipliant l’entier par 125 ms, vous obtiendrez la durée de la note.
Ex : sol 4 signifie qu’il faut jouer la note « sol » pendant 500 ms.

Adapter le code précédent pour jouer cette nouvelle mélodie.
Après le premier split, vous devrez utiliser un 2e split sur chaque élément pour séparer la note et la durée.
Ex : En appliquant un split sur sol 4 , on obtient la note sol et la durée 4.

melodie3 = "sol 4, sol 4, sol 4, mi 3, si 1, sol 4, mi 3, si 1, sol 8"

Petite correction pour les oreilles absolues : le « si » est plutôt un « si bémol ».

Distance (Ultrason)

4-pin

⚠ Bien enlever le jumper au dos pour être en mode HC-SR04. (Le jumper active le mode UART)

Notez que :
* a trigger = un déclencheur
* an echo = un rappel/retour

from microbit import *
from machine import time_pulse_us
import utime

################################
## 4-pin : avec ECHO et TRIG ###
################################

### Constantes
TRIG_PIN = pin1
ECHO_PIN = pin2

### Définition d'une fonction
def mesure_distance():
    ### On envoie une impulsion de 10 µs sur la broche TRIG
    ### pour démarrer une salve d'ultrasons
    TRIG_PIN.write_digital(1)
    utime.sleep_us(10) # on attend pendant 10 µs
    TRIG_PIN.write_digital(0)
    # fin de l'impulsion

    ### Lecture du résultat et calcul de la distance
    # configuration de la broche ECHO en mode INPUT
    ECHO_PIN.read_digital()
    # Puis time_pulse_us attend que la broche ECHO passe à 1 puis
    # renvoie la durée en µs pendant laquelle la broche est restée à 1
    duree = time_pulse_us(ECHO_PIN, 1)
    # Calcul la distance en cm
    distance = int(duree * 0.01715)
    return distance


### Programme principal
while True:
    # bouton A pour effectuer et afficher une mesure
    if button_a.was_pressed(): 
        d = mesure_distance()
        # affichage dans la console REPL de l'éditeur
        print(d)
        # affichage sur la carte micro:bit
        display.show(' '+str(d)+" # ", delay=500, loop=True, wait=False)

    # bouton B pour effacer
    if button_b.was_pressed():
        display.clear()

3-pin

from microbit import *
from machine import time_pulse_us
##################################
## 3-pin : broche SIG sur pin2 ###
##################################
### Définition d'une fonction
def mesure_vitesse(distance):
    """ Calcule la vitesse du son en m/s
    sur une distance 'distance' exprimée en cm """
 
    # envoie une impulsion de 10 ms sur pin2 pour démarrer une salve US #
    pin2.write_digital(1)
    sleep(10)
    pin2.write_digital(0)
    # fin de l'impulsion

    # configuration de pin2 en INPUT
    pin2.read_digital()
    # attend que pin2 passe à 1 puis renvoie la durée 
    # en µs pendant laquelle pin2 reste à 1
    duree = time_pulse_us(pin2, 1)

    # calcul de la vitesse (cm/s) grâce à la duree aller-retour (µs) : 
    vitesse = 2 * distance / (duree/1000000)
    # conversion en m/s (et arrondie)
    vitesse = round(vitesse/100) 
    return vitesse

### Programme principal
# distance utilisée pour mesurer la vitesse du son .. en cm !
DISTANCE = 50 
while True:
    # bouton A pour effectuer une mesure
    if button_a.was_pressed():
        vitesse = mesure_vitesse(DISTANCE)
        # retour REPL dans la console de l'éditeur
        print(vitesse)
        display.show("v="+str(vitesse)+" ", delay=500, loop=True, wait=False)
    
    # bouton B pour effacer
    if button_b.was_pressed():
        display.show(" "+str(vitesse)+" ", delay=500, loop=True, wait=False)

Carte moteur motor:bit

carte Motor:Bit de ELECFREAKS
  • Motor Drive Chip: TB6612
  • 2 voies pour des moteurs TT
  • chaque moteur alimenté à 1.2A maximum
  • un buzzer contrôlé par P0.

Broches bleues SVG

Si le commutateur VCC (juste au-dessus) est sur 5V, alors les broches rouges donnent du 5V et le niveau de signal des broches bleues est sur 5V aussi.
Si le commutateur VCC est sur 3.3V, alors les rouges délivrent du 3.3V et le niveau du signal des bleues est aussi 3.3V.

  • P13-P16 : 4 ports GPIO
  • P19-P20 : 1 connecteur de communication IIC

Broches jaunes SVG

  • P3-P7, P9-P11 : 8 broches alimentées en 3.3V .
  • P3, P4, P10 : peuvent être utilisées en mode « Entrée de signal analogique ».

ATTENTION : pour utiliser les broches P3, P4, P6, P7, P9, P10 (dédiées aux 25 leds), il faut ajouter :

  • display.off() pour désactiver l’affichage
  • display.on() pour réactiver les leds

Broches Moteurs

P8Direction de M1sens positif de rotation en tension HIGH (digital = 1);
sens négatif sur LOW (digital = 0).
P1Vitesse de M1PWM (analog = entre 0 et 1023)
P2Vitesse de M2PWM (analog = entre 0 et 1023)
P12Direction de M2sens positif de rotation en tension HIGH;
sens négatif sur LOW.
from microbit import *
 
def avance(v):
    # le mode "avance" à la vitesse v (entier entre 0 et 1023)
    pin8.write_digital(1)  # direction M1
    pin12.write_digital(0) # direction M2
    pin1.write_analog(v) # vitesse M1
    pin2.write_analog(v) # vitesse M2
 
def recule(v):
    pin8.write_digital(0)  # direction M1
    pin12.write_digital(1) # direction M2
    pin1.write_analog(v) # vitesse M1
    pin2.write_analog(v) # vitesse M2
 
avance(300) # le robot avance pendant 2s (vitesse faible)
sleep(2000)
recule(300) # le robot recule pendant 2s
sleep(2000)
avance(0)   # le robot s'arrête

Carte Joystick:bit

Joystick:bit v2.0 de ELECTROFREAKS
  • pin2 : pin2.read_analog() détecte le bouton pressé
    buttons = {2: "A", 517: "B", 686: "C", 769: "D", 853: "E", 820: "F", 1021 : "aucun"}
  • pin0 et pin1 donnent la position du joystick :
    • pin0.read_analog() sur X : 3~1021 et Xcentre = 529
    • pin1.read_analog() sur Y : 3~1021 et Ycentre = 506

/!\ Ces valeurs sont approximatives car elles varient d’une carte Joystick:bit à l’autre !

Tester la Joystick:bit

Les coordonnées du joystick
from microbit import *
import radio
#######################################
# TEST : JOYSTICK:BIT
while True:
    press = pin2.read_analog()
    print("bouton : "+ str(press))
    X = pin0.read_analog()
    Y = pin1.read_analog()
    print("joystick : "+str(X)+" , "+str(Y))
    sleep(100)

Emetteur radio (carte Joystick:bit)

from microbit import *
import radio

#######################################
# JOYSTICK:BIT
def button_press():
    press = pin2.read_analog()
    if press > 938:  # le plus fréquent car aucun bouton : press=1021
        return ""
    elif press < 256:
        return "A"
    elif press < 597:
        return "B"
    elif press < 725:
        return "C"
    elif press < 793:
        return "D"
    elif press < 836:
        return "F"
    else:
        return "E"

def joystick_push():
    # Par défaut : x = 529 (3~1021)   y = 506 (3~1022)
    # map (1~1023) to (-1022~1022)
    x = 2 * pin0.read_analog() - 1024
    y = 2 * pin1.read_analog() - 1024
    if -100 < x < 100:
        x = 0
    if -100 < y < 100:
        y = 0
    return x, y


radio.config(channel=7, group=0, queue=1, power=7)
radio.on()
while True:
    X, Y = joystick_push()       
    message = str(X) + "|" + str(Y) + "|" + button_press()
    radio.send(message)      # ex : "-700|400|"

Récepteur radio (carte motor:bit)

from microbit import *
import radio

###################################
# MOTOR:BIT   M1=gauche   M2=droite

def drive(vitesseX, vitesseY): # vitesse : -1023 ~ 1023
    if vitesseX < 0:
        vitesseX = - vitesseX
        pin8.write_digital(0)  # direction M1
    else:
        pin8.write_digital(1)  # direction M1

    if vitesseY < 0:
        vitesseY = - vitesseY
        pin12.write_digital(1)  # direction M2
    else:
        pin12.write_digital(0)  # direction M2

    if vitesseX > 900:
        vitesseX = 900
    if vitesseY > 900:
        vitesseY = 900
    pin1.write_analog(vitesseX) # vitesse M1
    pin2.write_analog(vitesseY) # vitesse M2


radio.config(channel=7, group=0, queue=1, power=7)
radio.on()
ancien_bouton = ""
while True:
    msg_recu = radio.receive()
    if msg_recu is not None:
        [joystickX, joystickY, bouton] = msg_recu.split("|")
        
        # moteur
        joystickX = int(joystickX)
        joystickY = int(joystickY)
        drive(joystickY + joystickX//3 , joystickY - joystickX//3)
        
        # bouton
        # /!\ redondances car un appui bref de btn A donne "A" "A" "A" "A"
        if bouton != ancien_bouton:
            # faire qqch
        ancien_bouton = bouton

Servo sur 5V

Code direct

Un servomoteur fonctionne par signal PWM (Pulse Width Modulation) à une fréquence de 50 Hz. Cela représente une période = 20 ms = 20 000 µs.
Sur chaque intervalle de 20 000 µs, c’est la durée du signal haut qui détermine l’angle. Pour la majorité des servo, cette durée est comprise entre 540 µs et 2400 µs.
Or write_analog a pour argument un entier entre 0 et 1023 (où 1023 est 100%).
Donc on recalcule les angles extrêmes ainsi :
1023 * 540 / 20000 = 28
1023 * 2400 / 20000 = 122

from microbit import *

pin16.set_analog_period(20) # 20 ms est la période PWM d'un servo

# La fourchette 540-2400 sur 20000 du servo est transposée
# en l'intervalle 28-122 sur 1023 de write_analog ainsi :
#  28 =>  547µs => angle minimum (0°) (tourne vers droite) 
#  76 => 1485µs => angle central (90°) 
# 122 => 2385µs => angle maximum (180°) (tourne vers gauche) 

pin16.write_analog(28) # tout à gauche
sleep(1000) # donne le temps au servo de tourner (0.24s pour 60°)
pin16.write_analog(122) # tout à droite
sleep(1000) # donne le temps au servo de tourner
pin16.write_analog(76) # au milieu
sleep(1000) # donne le temps au servo de tourner

pin16.write_digital(0)  # éteint la broche

En utilisant une classe Servo

Il existe une classe pour les servo en micropython disponible sur cette page, cependant il faut ajouter un sleep() entre write_analog et write_digital(0) .

from microbit import *
class Servo:
    """
    Args:
        pin (pin0 .. pin3): The pin where servo is connected.
        freq (int): The frequency of the signal, in hertz.
        min_us (int): The minimum signal length supported by the servo.
        max_us (int): The maximum signal length supported by the servo.
        angle (int): The angle between minimum and maximum positions.
    Usage :
        sv1 = Servo(pin0)   # ou sv1 = Servo(pin0, min_us=1000, max_us=2000)
        sv1.write_angle(50) # turn servo to 50 degrees 
    """
    def __init__(self, pin, freq=50, min_us=600, max_us=2400, angle=180):
        self.min_us = min_us
        self.max_us = max_us
        self.us = 0
        self.freq = freq
        self.angle = angle
        self.analog_period = 0
        self.pin = pin
        analog_period = round(1000/self.freq)   # hertz to milliseconds
        self.pin.set_analog_period(analog_period)

    def write_us(self, us):
        us = min(self.max_us, max(self.min_us, us))
        duty = round(us * 1024 * self.freq // 1000000)
        self.pin.write_analog(duty)
        sleep(750)
        self.pin.write_digital(0)  # turn the pin off

    def write_angle(self, degrees=None):
        degrees = degrees % 360
        total_range = self.max_us - self.min_us
        us = self.min_us + total_range * degrees // self.angle
        self.write_us(us)

sv = Servo(pin1, min_us=750, max_us=2250)
t = 90
while True:
    if button_a.was_pressed():
        t = t + 5
        sv.write_angle(t)
Mu editor

Mu editor

Téléchargement

  • Pour corriger la faute d’orthographe « # Ecris ton programme » : ‘mu.mo’ line 82 
    (C:\Program Files (x86)\Mu\pkgs\mu\locale\fr\LC_MESSAGES\mu.mo)

Edition

  • CTRL+F : find and replace (rechercher et remplacer)
  • CTRL+K : Commente ou dé-commente la ligne actuelle .. ou les lignes sélectionnées.
  • TAB : Indente la ligne actuelle .. ou les lignes sélectionnées.
  • SHIFT+TAB : Désindente la ligne actuelle .. ou les lignes sélectionnées.
  • CTRL+Z : Annuler (Maintenir appuyé pour continuer d’annuler)
  • CTRL+Y : Rétablir (Maintenir appuyé pour continuer de rétablir)
  • CTRL+A : Sélectionner tout
  • CTRL+X : Couper
  • CTRL+SHIFT+V : copier dans REPL
  • CTRL+GAUCHE / DROITE : Avance le curseur d’un mot
  • SHIFT+HAUT / BAS / GAUCHE / DROITE : Sélectionne le texte (caractère par caractère)
  • CTRL+SHIFT+GAUCHE / DROITE : Sélectionne le texte (mot par mot)

Console REPL

REPL = Read-Evaluate-Print-Loop

Raccourci ‘copier‘ : CTRL+SHIFT+V (dans REPL)


Cliquer sur REPL crée une interruption clavier .. vous pouvez alors :

  • afficher des valeurs en console : il faut faire Ctrl+D (soft reboot) pour redémarrer le programme.
  • ou intéragir avec la micro:bit en tapant des commandes python en console, la carte exécutera ces instructions immédiatement.

Afficher les valeurs en console

Si on ne souhaite pas taper de commande alors il faut redémarrer la micro:bit avec Ctrl+D (soft reboot) ou le bouton RESET de la carte micro:bit.

print('résultat = '+str(x)) affiche la valeur de x

Interagir avec la micro:bit

Liaison UART (PC – micro:bit)

La transmission de la micro:bit vers le PC est une liaison Série utilisant le module UART.

  • Baud rate  = 115200
  • Data = 8 bits
  • Parity  = none
  • Stop  = 1 bit

Graphique (Plotter)

Graphique (Plotter) récupère les valeurs numériques de type tuple de la console REPL et dresse un graphique.

Exemples de tuples :

  • (50,) est un tuple avec un élément
  • (12,-15) est un tuple avec 2 éléments

Exemple 1 : la gravité (accéléromètre)

Mode d’emploi :

  1. Dans le code, ajouter un affichage console de tuple.
    Par exemple : print( (accelerometer.get_x(), ))
    ou print( accelerometer.get_values() )
  2. Appuyez sur le bouton Graphique de Mu et le tracé s’effectue en temps réel.
  3. Facultatif : Appuyer sur le bouton REPL pour visualiser les tuples.

Exemple : Les effets de la gravité terrestre sur l’accéléromètre de la micro:bit
x = bleu | y = vert | z = orange .. mesurés en milli-g
(rappel : 1g = la force de la gravité de la Terre).

from microbit import *

xSeulmt = True
while True:
    sleep(10) # on attend 10 ms
    if button_a.was_pressed():
        xSeulmt = not xSeulmt

    if xSeulmt:
        print( (accelerometer.get_x(), ) )
    else:
        print( accelerometer.get_values() )

Exemple 2 : la boussole (magnétisme)

Approcher/éloigner la carte d’un aimant.

from microbit import *

while True:
    sleep(200)
    print((compass.get_field_strength(),)) #tuple 1 valeur

Récupérer les data

Où ?

Les data sont stockés dans le répertoire mu_code sous forme de fichier CSV lorsque Graphique (Plotter) est activé : C:\Users\<votre nom>\mu_code\data_capture

Excel : problème & solution

Problème : En ouvrant le fichier sous Excel, on remarque 2 soucis : un tuple 3-valeurs est mis sur 1 colonne (et non 3 colonnes) et il y a des lignes vides à supprimer.

Solution : Pour que les tuples s’écrivent sur 3 colonnes, on va changer le séparateur virgule  , en point virgule ; dans le fichier *.csv avec Notepad++ :

  1. Ouvrir le fichier *.csv dans Notepad++.
  2. Faire Ctrl+H ou menu Rechercher > Remplacer
  3. Remplir les champs :
    Rechercher : ,
    Remplacer par : ;
    Puis clic sur ‘Remplacer tout
  4. Autant enlever les lignes vides aussi, donc on recommence :
    Faire Ctrl+H ou menu Rechercher > Remplacer :
    Rechercher : \r
    Remplacer par : ne rien mettre
    Cocher : Mode étendu (\n,\r,..)
    Puis clic sur ‘Remplacer tout
  5. Enregistrer le fichier puis l’ouvrir avec Excel.