Dans le cas de développement de code C/C++ sur systèmes embarqués, notamment, l’optimisation et le test du code sont des points essentiels afin de limiter l’empreinte mémoire et le temps d’exécution.
Optimisation du code C pour les systèmes embarquées
L’optimisation du code C est nécessaire pour les systèmes embarqués car, afin de gagner sur le coût du hardware, on prévoit le strict nécessaire en terme d’interface, de mémoire et de capacité de calcul. Pour un ordinateur fixe, on va tout de même préférer des temps d’exécution faibles afin de ne pas encombrer la charge CPU. L’optimisation va avoir deux objectifs concurrentiels
réduire la quantité de mémoire utilisée par un programme
augmenter la vitesse d’exécution des fonctions du programme
Une programme peut être le plus rapide possible ou le plus petit possible mais pas les deux. L’optimisation d’un des critères peut fortement impacter l’autre.
Le compilateur se charge souvent d’optimiser le code mais il est primordiale de l’optimiser manuellement. On va généralement se concentrer sur l’optimisation des sections critiques du code et laisser le compilateur se charger du reste.
Même s’il y a de bonnes pratiques à garder à l’esprit lorsqu’on développe un code, un code ne devrait être otpimisé que lorque c’est strictement nécessaire (limite mémoire, lenteur d’exécution). Un code doit avant tout être lisible, maintenable et fonctionnel.
Améliorer l’efficacité du code
Les fonctions inline
Le mot clé inline permet de spécifier au compilateur de remplacer un appel à la fonction par le code de la fonction. Lorsqu’une fonction est présente dans quelques sections de code mais appelée un grand nombre de fois, transformer la fonction en fonction inline peut améliore les performances d’exécution du code
Les tables lookup
Les lookup permettent de remplacer des fonctions demandant un calcul compliqué par une simple association de variables. On peut ainsi remplacer une fonction sin() ou des sections swtich par des tables à choix multiples.
Code assembleur manuel
Un compilateur transforme le code C en code assembleur optimisé. Un développeur confirmé peut, lorsque la fonction est critique, créer son propre code assembleur.
Réduire la taille du code (ROM)
Taille et type de variable
Choisir correctement la structure, le type et la taille de variable nécessaire pour stocker les données améliore considérablement les performances du code.
Goto statement
La fonction goto permet d’éviter des algorithmes d’arborescences compliqués. Cela rend le code plus difficile à lire et peut être la source de plus d’erreur.
Éviter les librairies standards
Les librairies standards sont généralement lourdes en taille et gourmandes en calculs car elles tentent de couvrir tous les cas. Développer ses propres fonctions correspondant à son strict besoin est un bon moyen d’optimiser son code C/C++
Réduire l’usage de la mémoire (RAM)
Mots-clés const et static
Une variables static correspond à une variable uniquement accessible dans le contexte d’une fonction mais dont l’état est maintenu entre les appels de la fonction. Cela permet de limiter l’utlisation de variable globale
Tester les performances du code C/C++
Mesurer le temps d’exécution d’une section de code à l’aide de la librairie time.h
Exemple de résultat de fichier perfo.txt: vous obtenez un tableau contenant le temps d’exécution cumulé sur la durée total d’exécution, le nombre d’appels et le temps d’exécution moyen.
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
37.21 4.26 4.26 2 2.13 3.95 func1()
31.79 7.90 3.64 1 3.64 3.64 new_func1()
30.83 11.43 3.53 func2()
0.17 11.45 0.02 main
0.00 11.45 0.00 1 0.00 0.00 count_to(int, int)
Utilisation de la mémoire
L »option de compilation –stats donne des statistiques générales sur le code
La deuxième vérifie la taille de la mémoire stack attendu pour une valeur données (32).
In file included from <command-line>:
./include/utils.cpp: In function 'void count_to(int, int)':
./include/utils.cpp:6:6: warning: stack usage is 64 bytes [-Wstack-usage=]
6 | void count_to(int n, int delay)
| ^~~~~~~~
helloworld.cpp: In function 'void new_func1()':
helloworld.cpp:7:6: warning: stack usage is 64 bytes [-Wstack-usage=]
7 | void new_func1(void)
| ^~~~~~~~~
helloworld.cpp: In function 'void func1()':
helloworld.cpp:17:6: warning: stack usage is 64 bytes [-Wstack-usage=]
17 | void func1(void)
| ^~~~~
helloworld.cpp: In function 'void func2()':
helloworld.cpp:28:13: warning: stack usage is 64 bytes [-Wstack-usage=]
28 | static void func2(void)
| ^~~~~
helloworld.cpp: In function 'int main()':
helloworld.cpp:40:5: warning: stack usage is 96 bytes [-Wstack-usage=]
40 | int main(void)
Simulation de l’empreinte mémoire sur un hardware spécifique
Installation de l’extension Code Runner. cette extension est facultative mais vous permettra de lancer un code dans un terminal externe pour utiliser les entrées utilisateurs (cin). Dans les paramètres, activez l’option Code-runner: Run In Terminal.
Installation d’un compilateur
Pour compiler, exécuter et analyser votre code vous aurez besoin d’outil comme GNU.
Téléchargez et dézipper une version de GNU Compiler Collection (GCC) s’il n’est pas déjà présent sur votre ordinateur.
g++ --version
Vous pouvez également installer une version de GCC à partir de MSYS2
$ pacman -S mingw-w64-ucrt-x86_64-gcc
Créer votre projet C++
Une fois les différentes extensions installées, vous pouvez créer votre projet C++. Pour cela, créez simplement un dossier, ouvrez VS Code sur ce dossier puis créer un fichier .cpp. Vous pouvez le faire via l’interface VS Code ou en ligne de commande
mkdir myproject
cd myproject
code .
Copier le code Hello world dans le fichier source.
Pour lancer le code vous pouvez utiliser le menu Run > Start Debugging (F5) qui lancera le programme dans la console de debug ou faire un clique-droit sur l’éditeur et lancer Run Code (Ctrl+Alt+N) qui le lancera dans le terminal (si Code runner est activé)
Vous pouvez également le faire en ligne de commande à partir du terminal
Vous pouvez ajouter des librairies externes ou créer vos propres librairies. Pour que VS Code compile les librairies avec le programme, vous devez spécifier le chemin d’accès dans le fichier tasks.json (ex: « –include=include/utils.cpp »)
pour compiler le programmer, il faut compiler et lier les deux fichiers
g++ helloworld.cpp include/utils.cpp -o helloworld
# or
g++ -c helloworld.cpp include/utils.cpp # get object files
g++ -o helloworld.exe helloworld.o include/utils.o
./helloworld.exe #run program
Mesurer le temps d’exécution du programme
Une méthode assez direct pour mesurer le temps d’exécution du programme est d’utiliser la librairie time.h et mesurer le temps écouler entre le début du programme et la fin.
Pour tester le temps d’exécution nous créons une fonction qui compte de 0 à n avec un certain délai entre chaque itération.
void count_to(int n = 10, int delay = 0);
#include <iostream>
#include <windows.h>
//#include <unistd.h>
void count_to(int n, int delay)
{
for (int i=0;i<=n;i++){
std::cout << i << std::endl;
Sleep(delay);
}
}
Puis dans le code principal, nous mesurons le temps avant et après l’exécution de la fonction count_to()
La fonction Run and Debug permet d’exécuter le code pas à pas en plaçant des points d’arrêts (breakpoints) à différents endroits du code (identifier à gauche du numéro de ligne par un point rouge). Il permet aussi d’observer les variables et registres au fur et à mesure de l’exécution. Vous pouvez ainsi suivre l’évolution du code pas à pas à la recherche d’erreur éventuelles.
Le code va s’exécuter normalement jusqu’au premier point d’arrêt, vous pouvez ensuite continuer la progression jusqu’au prochain point d’arrêt, ou exécuter le code pas à pas à partir de ce point à l’aide des boutons de navigations
Vous pouvez observer les valeurs des variables ou des registres à chaque étapes dans l’onglet VARIABLES. Ou observer certaines variables dans WATCH.
Vous pouvez également observer le code assembleur compilé à ouvrant la vue « disassembly » à l’aide de clique-droit sur l’éditeur puis « Open Disassembly View ».
Troubleshoot
« thread » n’est pas reconnu
essayer d’installer MinGW-w64 à partir de MSYS2 et d’ajouter C:\msys64\ucrt64\bin aux variables d’environnement. Vérifier que le compilateur utilisé est g++
Utilisation de cout lève une erreur segmentation
comme solution intermédiaire, vous pouvez remplacer le cout par des printf
vérifier que la librairie libstdc++-6.dll se trouve au même endroit que le compilateur à l’aide la console de debug de vs code
L’éditeur de code VSCode permet de créer et développer des projets dans différents langages de programmation comme Python ou C++ grâce à de nombreuses extensions.
Installation de VSCode
Si ce n’est pas déjà fait, téléchargez et installez VSCode
L’utilisation d’un éditeur de code comme VSCode fait partie des bonnes pratiques pour gagner en productivité sur vos projets Python.
Vous pouvez installer des extensions pour vous aider dans la programmation comme Python ou Python Debugger
Créer et configurer un projet Python
Pour créer un projet, vous pouvez soit naviguer vers un dossier contenant vos projet et lancer VSCode à partir de cet emplacement dans un terminal
mkdir tuto
cd tuto
code .
Ou ouvrir un dossier à partir de VSCode.
A l’aide Command Palette, créer un environnement virtuel qui vous permettra de conserver une installation propre de Python et ses paquets pour votre projet. Python : Create Environment … , sélectionnez Venv puis choisissez la version de Python désirée
Une fois l’environnement créé, vous pouvez vérifier la configuration de l’interpréteur de l’environnement. Dans le terminal de VSCode, entrez les commandes suivantes
python --version
python -m pip freeze
La commande pip freeze devrait renvoyer une valeur vide puisqu’il n’y a pas de paquets installés sur un environnement virtuel neuf.
Créer et exécuter un script Python avec VSCode
Créer ensuite un fichier script.py, avec le code print(« hello world ») que vous pouvez exécuter à l’aide d’un clique-droit sur la page d’édition Run Python > Run Python File in Terminal
Dans ce tutoriel, nous allons voir comment programmer un ESP32 à l’aide de l’environnement ESP-IDF avec VSCode. ESP-IDF ou Espressif IoT Development Framework, est l’environnement officiel de programmation des ESP32 et permet la programmation d’application de qualité en C et C++.
ESP-IDF vs ESP Arduino Core
Nous avons vu dans un précédent article comment programmer un NodeMCU ESP32 avec Arduino IDE. L’environnement ESP Arduino a été développé pour simplifier la programmation des ESP32. Elle permet aux débutants et amateurs, déjà utilisateurs des Arduino, d’utiliser les mêmes outils pour le développement de projets basés sur des ESP32. Toutefois, il faut rappeler que l’Arduino IDE utilise une version simplifiée et incomplète du langage C/C++ ce qui peut limiter les fonctionnalités et performances du code développé à l’aide de cet outil.
L’environnement ESP-IDF, quant à lui, est totalement compatible avec les standards C/C++ et propose un ensemble de librairies et de fonctionnalités spécifiques pour le développement d’objets connectés avec ESP32.
Gestion Wifi et Bluetooth
Programmation OTA
Fonction temps-réel avec FreeRTOS
Gestion des capteurs et protocoles de communication les plus courants
ESP-IDF propose également une compatibilité avec toutes les versions de microcontrôleur ESP32.
Rechercher et installer l’extension ESP-IDF pour VSCode qui contient des librairies et configuration de base pour la programmation d’ESP32
Une fois l’extension installée, vous pouvez la configurer avec la palette de commande. Dans le menu, View>Command Palette…, cherchez « ESP-IDF Configure ». Sélectionner la version d’ESP-IDF à installer. La configuration installera les modules suivants:
ESP-IDF
ESP-IDF tools
Python venv pour ESP-IDF
Configurer un projet pour ESP32
Pour charger votre premier code, vous pouvez utiliser l’exemple Blink
Sélectionnez View -> Command Palette… et entrez ESP-IDF: Show Examples Projects. Sur la page qui s’ouvre, trouvez et appuyez sur le bouton Show Examples. Il vous sera alors demandé de sélectionner le framework installé précédemment.
Dans la liste des exemples, vous pouvez ensuite sélectionner Blink et appuyer sur le bouton « Create project using example Blink »
Vous pouvez également créer un nouveau projet. ESP-IDF: New Project . Sélectionner ensuite Extensions>template-app pour un projet vierge. Le fichier main se trouve sous myproject/main/main.c
N.B.: avec le templae arduino-as-a-component, vous pouvez écrire avec le langage Arduino dans le framework ESP-IDF
Voici un exemple Hello world, pour tester la configuration du projet.
Lorsque les fichiers du projet sont édités, vous pouvez suivre les étapes suivantes pour configurer le projet
sélectionner le type de carte ESP-IDF: Set Espressif device target
sélectionner le port série ESP-IDF: Select port to use (COM, tty, usbserial)
Pour sélectionner le type et la carte utilisée. Dans Command Palette, entrez Set Espressif device target. Dans notre cas, ESP32-WROOM-32, esp32 puis ESP32 chip (via ESP USB bridge).
N.B.: une fois la carte sélectionnée, vous pourrez utiliser les librairies de base
Sélectionner le port série à l’aide de la commande ESP-IDF: Select port to use (COM, tty, usbserial)
Compiler, téléverser le code sur un ESP32
Une fois le projet configuré et le code créé, nous allons suivre un certain nombres d’étapes pour pouvoir téléverser le code sur la carte ESP32:
compiler le projet ESP-IDF: Build your project
téléverser le code ESP-IDF: Flash your project
déverminer le code ESP-IDF: Monitor your device
Il est possible de combiner les 3 dernières commandes avec ESP-IDF: Build, Flash and start a Monitor on your device
Maintenant que l’environnement de programmation est prêt, vous pouvez compiler le programme. Dans Command Palette, rechercher ESP-IDF: Build your project.
Pour flasher le programme sur la carte, vérifier le port série sélectionné puis flasher le code ESP-IDF: Flash your project. Dans notre cas, la programmation se fait par le port USB, nous utilisons donc le mode UART.
Une fois le code téléverser, vous pouvez observer les sorties sur le port série à l’aide du moniteur ESP-IDF: Monitor your device
Ajouter des librairies à votre code
Certaines librairies existent dans le framework ESP-IDF, pour trouver les composants disponibles vous pouvez entrer ESP-IDF: Show Component Registry dans Command Palette
Une fois votre composant trouvé, vous pouvez l’ajouter à votre projet en utilisant le bouton « install » ou la ligne de commande
idf.py add-dependency "espressif/button^3.2.0"
N.B.: une fois le composant installé, vous pouvez accéder à un exemple dans managed_components>espressif_button> examples
Vous pouvez alors développer votre propre code à partir de la librairie
Lors de l’exécution du code, si vous appuyez une ou deux fois sur le bouton boot pour voir le code spécifique s’afficher
I (341) main_task: Returned from app_main()
I (3611) myproject example: Button event BUTTON_PRESS_DOWN (0)
I (3821) myproject example: Button event BUTTON_PRESS_UP (1)
I (4011) myproject example: Button event BUTTON_SINGLE_CLICK (4)
I (4011) myproject example: Execute code if single click
I (4011) myproject example: Button event BUTTON_PRESS_REPEAT_DONE (3)
I (12231) myproject example: Button event BUTTON_PRESS_DOWN (0)
I (12371) myproject example: Button event BUTTON_PRESS_UP (1)
I (12461) myproject example: Button event BUTTON_PRESS_DOWN (0)
I (12461) myproject example: Button event BUTTON_PRESS_REPEAT (2)
I (12601) myproject example: Button event BUTTON_PRESS_UP (1)
I (12781) myproject example: Button event BUTTON_DOUBLE_CLICK (5)
I (12781) myproject example: Execute code if double click
I (12781) myproject example: Button event BUTTON_PRESS_REPEAT_DONE (3)
I (14051) myproject example: Button event BUTTON_PRESS_DOWN (0)
I (14171) myproject example: Button event BUTTON_PRESS_UP (1)
I (14261) myproject example: Button event BUTTON_PRESS_DOWN (0)
Vous pouvez créer vos propres librairies en utilisant la commande ESP-IDF: Create New ESP-IDF component. Ceci va créer un dossier components/mycomponent dans lequel vous pourrez éditer les fichiers .h et .c.
Nous allons voir dans ce tutoriel comment mettre en place une cartographie avec un capteur Lidar sous Python. Le capteur lidar permet de se repérer dans l’espace et de cartographier.
Description du capteur Lidar
Le capteur lidar YLidarX4 est un capteur laser de distance couplé à un moteur qui le fait tourner. Il se comporte comme un radar permettant la détection d’obstacles à 360 degrés et de se fait une cartographie de l’espace. Il s’utilise souvent en robotique pour cartographier, se repérer dans un environnement et permettre un déplacement autonome.
Le capteur Lidar utilise une carte d’interface série permettant de le connecter à un ordinateur via un port USB.
Pour savoir sur quel port se connecte l’appareil, vous pouvez aller dans le gestionnaire de périphérique dans la section Ports (COM et LPT) (ici com3)
Test du Lidar avec l’outil officiel Tool
Vous pouvez télécharger l’interface Tool sur le site officiel. Cet outil permet de visualiser la cartographie obtenu à l’aide du Lidar.
Une fois le logiciel lancé, il suffit de sélectionner le port du périphérique et le modèle de Lidar. Une fois le Lidar connecté, vous pouvez lancer l’acquisition.
Installation du paquet Python
Pour utiliser le capteur Lidar avec Python, nous utilisons la librairie PyLidar3
python -m pip install Pylidar3
Script de test du Lidar
Pour vérifier l’installation de Pylidar, vous pouvez utiliser le script suivant qui affiche simple les informations de l’appareil.
import PyLidar3
port = "COM3" #input("Enter port name which lidar is connected:") #windows
Obj = PyLidar3.YdLidarX4(port)
print("connecting to {}".format(port))
ret = Obj.Connect()
if(1):
print(ret)
print("device info: ",Obj.GetDeviceInfo())
Obj.Disconnect()
else:
print("Error connecting to device")
Affichage du résultat du scan
Pour notre première cartographie, nous allons initialiser l’objet PyLidar3 et nous allons nous connecter à l’appareil
Obj = PyLidar3.YdLidarX4(port)
ret = Obj.Connect()
Nous allons ensuite définir quelques paramètres du scan:
durée du scan scanDuration
seuil de donnée à enregistrer dataThr
limite de la fenêtre winLim
Enfin nous allons lancer la mesure lidar et afficher l’acquisition avec matplotlib
import PyLidar3
import matplotlib.pyplot as plt
import math
import time
#init lidar
port = "COM3" #input("Enter port name which lidar is connected:") #windows
Obj = PyLidar3.YdLidarX4(port) #PyLidar3.your_version_of_lidar(port,chunk_size)
ret = Obj.Connect()
print(ret)
#ret = Obj.Connect()
#print(ret)
#parameters
scanDuration = 30 #scan for 30 seconds
dataThr = 1000 # disgard data below this value
winLim = 5000 # window limit in X and Y
#init data list on 360deg
x=[]
y=[]
for _ in range(360):
x.append(0)
y.append(0)
if(1):
deviceInfo = Obj.GetDeviceInfo()
print("device info : ",deviceInfo)
gen = Obj.StartScanning()
t = time.time() # start time
while (time.time() - t) < scanDuration:
data = next(gen)
for angle in range(0,360):
if(data[angle]>dataThr):
#x[angle] = data[angle] * math.cos(math.radians(angle))
#y[angle] = data[angle] * math.sin(math.radians(angle))
x[angle] = (x[angle] + data[angle] * math.cos(math.radians(angle))) / 2
y[angle] = (y[angle] + data[angle] * math.sin(math.radians(angle))) / 2
plt.clf()
plt.axis([-winLim,winLim,-winLim,winLim])
plt.title("Model: X4 Firmware: {} Hardware: {} SN: {}".format(deviceInfo['firmware_version'], deviceInfo['hardware_version'],deviceInfo['serial_number']))
plt.scatter(y, x, c='r', s=8)
plt.draw()
plt.pause(0.1)
Obj.StopScanning()
Obj.Disconnect()
else:
print("Error connecting to device")
plt.show()
Résultat
Une fois le script lancer, nous pouvons voir le process de cartographie en action et la carte se mettre à jour en temps réel.
Gestion du lidar avec threading
Il est possible de gérer la mesure dans un process différent de l’affichage pour une meilleure fluidité. A titre d’exemple, nous utilisons la librairie threading. Nous allons créer une fonction d’acquisition des données scan() que nous plaçons ensuite dans un thread.
threading.Thread(target=scan).start()
Nous pouvons ainsi traiter les données de cartographie sans perturber l’acquisition de la mesure.
import PyLidar3
import matplotlib.pyplot as plt
import math
import time
import numpy as np
import threading
#init lidar
port = "COM3" #input("Enter port name which lidar is connected:") #windows
Obj = PyLidar3.YdLidarX4(port) #PyLidar3.your_version_of_lidar(port,chunk_size)
ret = Obj.Connect()
print(ret)
#ret = Obj.Connect()
#print(ret)
#parameters
scanDuration = 30 #scan for 30 seconds
dataThr = 1000 # disgard data below this value
winLim = 5000 # window limit in X and Y
buffnum = 1 # matrix size to average position
#init data list on 360deg
x = np.zeros((buffnum, 360))
y = np.zeros((buffnum, 360))
def scan():
nb=0
while is_scanning:
data = next(gen)
if nb>=buffnum-1:
nb=0
else:
nb+=1
for angle in range(0,360):
if(data[angle]>dataThr):
x[nb][angle] = data[angle] * math.cos(math.radians(angle))
y[nb][angle] = data[angle] * math.sin(math.radians(angle))
if(1):
deviceInfo = Obj.GetDeviceInfo()
print("device info : ",deviceInfo)
gen = Obj.StartScanning()
t = time.time() # start time
is_scanning = True
threading.Thread(target=scan).start()
while (time.time() - t) < scanDuration:
xmean = np.mean(x, axis=0)
ymean = np.mean(y, axis=0)
plt.clf()
plt.axis([-winLim,winLim,-winLim,winLim])
plt.title("Model: X4 Firmware: {} Hardware: {} SN: {}".format(deviceInfo['firmware_version'], deviceInfo['hardware_version'],deviceInfo['serial_number']))
#plt.scatter(y,x,c='r',s=8)
plt.scatter(ymean,xmean,c='r',s=8)
plt.draw()
plt.pause(0.05)
is_scanning = False
Obj.StopScanning()
Obj.Disconnect()
else:
print("Error connecting to device")
plt.show() #keep figure open at the end
Le microordinateur Raspberry Pi a différentes utilisations courantes dont le retro-gaming avec RetroPie. Nous allons voir dans ce tutoriels, comment installer, configurer et utiliser RetroPie sur un Raspberry Pi
Matériel
Raspberry Pi 4 ou autre carte compatibles
écran+clavier+souris
manette de jeux vidéo ou autre capteur
Quelques mots sur RetroPie
RetroPie est un logiciel qui s’intègre à un OS et permet de transformer sa machine en émulateur de console de jeux vidéo. Il permet donc de faire du retro-gaming et de rassembler sur une même machine d’anciens jeux vidéo qui étaient disponibles sur diverses consoles.
RetroPie est compatible avec les cartes suivantes
Raspberry Pi Zero/Zero2W/1/2/3/4/400 (Raspbian)
PC linux (Debian/Ubuntu)
Odroid-C1/C2 (Ubuntu)
ODroid-XU3/XU4 (Ubuntu)
L’installation de RetroPie sur un Raspberry Pi permet d’en faire une console de jeux vidéo pour toute la famille à moindre coût. Cela permet aussi de réaliser une borne d’arcade.
Téléchargement et installation d’un OS avec RetroPie (conseillé)
Pour installer un OS contenant RetroPie, vous pouvez télécharger une image correspondant à votre machine sur le site officiel et l’écrire sur une carte SD via Raspberry Pi Imager ou Balena Etcher.
Vous pouvez également utiliser le logiciel Raspberry Pi Imager et sélectionner l’OS souhaité pour le retro-gaming
Une fois l’image écrite sur la carte SD, vous pouvez l’insérer dans le Raspberry Pi puis allumer le Raspberry Pi. L’installation de RetroPie devrait se lancer automatiquement.
Configuration de la manette de jeux
Au lancement de EmulationStation, la première chose demandé est la configuration de la manette de commande de jeux qui vous servira pour naviguer à travers les menus. Branchez une manette de jeux et suivez les instructions. Vous pourrez changer ces paramètres plus tard.
Configurer votre station d’émulation
Une fois les commandes de votre manette définies pour pouvoir naviguer dans le menu de RetroPie, vous pouvez configurer votre système en fonction de votre besoin;
audio
localisation
configuration WiFi
activation SSH
mise à jour
Installation RetroPie packages
Une fois votre système configuré, il est temps d’installer des jeux sur votre émulateur.
Où trouver et comment Transférer des ROMs pour RetroPie
Les ROMS (Read-only Memory) sont des fichiers contenant les images de jeux vidéo. Il est possible de trouver des roms sur la toile. Voici quelques sites dont le premier est le plus sûr
N.B.: attention au licence d’utilisation. Il peut être illégal de télécharger certains jeux sous licence.
Une fois les fichiers téléchargés, créez une arborescence de dossier avec un dossier par console ~/retropie/roms/$CONSOLE (ie: atari ou gb). Vous pouvez copier les fichiers décompressés dans le dossier de la console en question.
N.B.: les fichiers reconnus par RetroPie ont généralement les extensions correspondant au console (.gb, .gba, etc.) ou des images de CD ISO (.img)
Pour transférer les jeux sur RetroPie, vous pouvez utiliser le dossier Samba partagé prévu sur RetroPie (\\RETROPIE\roms)
Ou utiliser une clé USB avec un dossier par console contenant les images des jeux vidéo . Lorsque vous insérez la clé USB dans le Raspberry Pi, le transfert devrait se faire automatiquement.
Pour que les jeux soient disponibles dans RetroPie, le logiciel EmulationStation doit être redémarré. (Menu > Quit > Restart EmulationStation )
Jouer et gérer les données de jeux
Une fois EmulationStation redémarré, les consoles et jeux disponibles apparaissent dans le menu principal.
Une fois le jeux sélectionné, il est possible de sortir du jeu, sauvegarder et charger avec les commandes suivantes (Hotkey default: bouton Select)
Hotkey Combination
Action
Hotkey+Start
Exit
Hotkey+Right Shoulder
Save
Hotkey+Left Shoulder
Load
Hotkey+Right
Input State Slot Increase
Hotkey+Left
Input State Slot Decrease
Hotkey+X
RGUI Menu
Hotkey+B
Reset
Terminal RetroPie
En cas de problème ou pour certaines opérations, vous aurez besoin de naviguer dans l’OS du RPi. Pour cela, il vous suffit de fermer EmulationStation (Menu>Quit>Exit EmulationStation)
Vous pourrez retrouver:
les fichiers Raspbian comme le fichier config.txt pour la configuration manuel du boot
sudo nano /boot/config.txt
les émulateurs installés
ls /opt/retropie/emulators
la configuration des joysticks
/opt/retropie/configs/all/retroarch/autoconfig
Problèmes courants
RetroPie ne boot pas
Si RetroPie ne boot pas du tout:
carte SD corrompue
mauvaise version de l’OS
Lors du premier démarrage, RetroPie reste bloqué sur le terminal de démarrage et EmulationStation ne se lance pas.
Dans ce tutoriel, nous allons apprendre comment gérer et tester la communication BLE (Bluetooth Low Energy) sur un ESP32 avec MicroPython.
Matériels
Un module ESP32
Un ordinateur avec Python installé
Câble USB pour la connexion ESP32-ordinateur
Un appareil Android
Environnement et Configuration de l’IDE
Pour communiquer et programmer en Python votre ESP32, vous pouvez suivre ce tutoriel précédent pour utiliser MicroPython.
Vous pouvez également installer l’application BLE Terminal sur votre téléphone Android pour tester la communication BLE
Activation du Bluetooth LE
Pour activer le BLE de votre EPS32, copier le code micropython suivant dans l’éditeur Thonny IDE et exportez le sur votre module. Dans cet exemple, nous utilisons la librairie bluetooth (autre option ubluetooth)
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html
import ubinascii
def main():
BLE = bluetooth.BLE()
BLE.active(True)
#Advertise
name = bytes("ESP32BLEmPy", 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
BLE.gap_advertise(100, adv_data)
#get MAC address
mac = BLE.config('mac')[1]
print("device MAC address is: "+ubinascii.hexlify(mac).decode())
#print("device MAC address is: "+mac.hex())
if __name__ == "__main__":
main()
N.B.: il est possible de récupérer l’adresse MAC au format hexadécimal en utilisant la fonction hex() ou la librairie ubinascii
Une fois le code chargé et l’annonce activé, vous pouvez retrouver l’appareil dans l’application BLE Terminal.
IMPORTANT: l’annonciation doit être activée après chaque déconnexion pour pouvoir reconnecter l’ESP32
Enregistrer des services
La communication BLE passe par le concept de services et caractéristiques avec certain droit d’accès (read, write, notify). Il existe des services par défaut (Nordic UART service (NUS), Heart rate (HR) ou vous pouvez créer vos propres services avec des identifiants uniques UUID.
Un service a un UUID unique et peut contenir différentes caractéristiques. Chaque caractéristique est définie par un UUID unique et différents droits d’accès.
La fonction bluetooth.UUID permet de définir les adresses des services et caractéristiques
bluetooth.FLAG_READ donne un accès en lecture au client
bluetooth.FLAG_NOTIFY permet de notifier le client sans action de sa part
bluetooth.FLAG_WRITE donne un accès en écriture au client
Nous pouvons ensuite utiliser définir les actions pour chaque évènement
#event callback function
def ble_irq(event,data):
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
print("BLE device connected successfully")
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
print("BLE device disconnected")
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
BLE.gap_advertise(100, adv_data)
elif event == _IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
conn_handle, attr_handle = data
print("write event: ",BLE.gatts_read(data[1]).decode('UTF-8').strip())
elif event == _IRQ_GATTS_READ_REQUEST:
# A client has issued a read. Note: this is only supported on STM32.
# Return a non-zero integer to deny the read (see below), or zero (or None)
# to accept the read.
conn_handle, attr_handle = data
print("read event: ",data)
Résultat
Avec ce code simple, vous pouvez vous connecter à l’appareil puis lire et écrire sur le service choisi dans l’application
MicroPython v1.22.1 on 2024-01-05; Generic ESP32 module with ESP32
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
device MAC address is: 3c6105315f12
>>> BLE device connected successfully
BLE device disconnected
BLE device connected successfully
write event: hello
read event: (0, 31)
Création d’une classe MicroPython pour gérer la communication BLE ESP32
Il est intéressant de créer une classe pour gérer la communication BLE que vous pourrez réutiliser dans différents projets. Dans le code de la classe, vous retrouvez tous les éléments décrit plus haut
initialisation et activation du BLE
définition des fonctions callback self.ble_irq
enregistrement des services self.register
annonciation self.advertise
Nous avons également rajouté une fonction de notification pour mettre à jour la valeur d’un capteur self.set_sensor
class ESP32BLE():
def __init__(self, name):
# Create BLE device management
self.name = name
self.ble = bluetooth.BLE()
self.ble.active(True)
#get MAC address
mac = self.ble.config('mac')[1]
print("device MAC address is: "+mac.hex())
self.ble.irq(self.ble_irq)
self.connections = set()
self.register()
self.advertise()
def ble_irq(self, event, data):
#define event callback functions
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
self.connections.add(conn_handle)
print("BLE device connected successfully")
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
self.connections.remove(conn_handle)
print("BLE device disconnected")
self.advertise()
elif event == _IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
conn_handle, attr_handle = data
print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip())
elif event == _IRQ_GATTS_READ_REQUEST:
# A client has issued a read. Note: this is only supported on STM32.
# Return a non-zero integer to deny the read (see below), or zero (or None)
# to accept the read.
conn_handle, attr_handle = data
print("read event: ",data)
def register(self):
#define services and characteristics
HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_UUID, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES)
self.ble.gatts_write(self.sen, str(43.256), False)
def advertise(self):
#advertise BLE module with name
name = bytes(self.name, 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
def set_sensor(self, data, notify=False):
# Data is sint16 with a resolution of 0.01.
self.ble.gatts_write(self.sen, str(data))
if notify:
for conn_handle in self.connections:
# Notify connected centrals to issue a read.
self.ble.gatts_notify(conn_handle, self.sen)
Nous allons voir dans ce tutoriel comment installer le logiciel Arduino IDE et CLI sur Raspberry Pi et ainsi combiner les forces des deux systèmes.
Vérifier la configuration de votre système
Pour savoir quelle version de l’IDE télécharger et installer, vous devez connaitre les spécificités de votre système. Notamment l’architecture du noyau (Linux kernel). Il existe plusieurs commandes possibles.
uname -a #display os and kernel info
cat /proc/version
architecture
kernel
i386/i486/i586/i686/armv7l
32 bit
x86_64(Intel)/aarch64(Arm)/armv8
64 bit
cat /proc/cpuinfo #cpu architecture
La commande la plus directe est de deamnder sur combien de bit est encodé une variable LONG
getconf LONG_BIT # result is 32 or 64
Installation d’Arduino IDE
Sur la page de téléchargement d’Arduino, sélectionnez la version correspondante à votre système et téléchargez-la.
Vous pouvez effectuer cette procédure en ligne de commande. en connaissant le nom du fichier arduino-1.8.19-linux<architecture>.tar.xz
pour mon système aarch64 :arduino-1.8.19-linuxaarch64.tar.xz
Voud devez alors accordez les droits en lecture et écriture
sudo chmod a+rw /dev/ttyACM0
Bonus: Installation de Arduino-CLI sur Raspberry Pi
Si votre système n’a pas accès à une interface graphique (headless, accès SSH), vous pouvez utiliser les lignes de commandes pour créer, compiler et téléverser vos scripts Arduino.
Un autre outil plus récent et plus puissant est Arduino-CLI que vous pouvez installer avec la commande
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
mv bin/* Arduino/
cd Arduino
chmod a+x arduino-cli
alias arduino-cli='sudo ./arduino-cli'
Mettre à jour la liste des cartes supportées
arduino-cli core update-index
Installer ensuite le gestionnaire de cartes
arduino-cli core install arduino:avr
Vérifiez les cartes connectées au Raspberry Pi et noter les noms FQBN
Pour récolter des données sur internet, il est possible de créer un Web crawler ou Web scraping avec Python. Un robot d’exploration du Web est un outil qui permet d’extraire des données d’une ou plusieurs pages Web.
Configuration de l’environnement Python
Nous partons du principe que Python3 et pip sont installés sur votre machine. Vous pouvez également utiliser un environnement virtuel pour conserver un projet propre et maitriser les versions de librairies de votre web crawler Python.
Nous allons tout d’abord installer la librairie requests qui permet de faire des requêtes HTTP au serveur pour récupérer les données.
python -m pip install requests
Pour analyser et naviguer dans les données du Web, nous utilisons la librairie Beautiful Soup qui permet de travailler avec des scripts à balises comme le HTML ou le XML
python -m pip install beautifulsoup4
Enfin, nous installons la librairie Selenium qui permet d’automatiser les tâches d’un navigateur Web. Elle permet d’afficher des pages web dynamiques et de réaliser des actions sur l’interface. Cette librairie permet à elle seule de faire du Web scraping sur internet car elle peut travailler avec un site web dynamique qui fonctionne avec JavaScript.
python -m pip install selenium
Pour faire fonctionner Selenium avec Mozilla, vous aurez besoin de télécharger Geckodriver
Récupérer une page Web avec resquest
Imaginons que nous souhaitions récupérer les données techniques d’une carte Arduino, nous pouvons charger la page désirée avec requests et bs4
En observant la structure de la page, vous pouvez repérer les balises, classes, identifiants ou textes qui vous intéressent. Dans cet exemple, nous récupérons
le nom de la carte
la description de la carte
N.B.: Vous pouvez retrouver la structure de la page web sur votre navigateur avec clique-droit sur la page puis « Inspecter »
import requests
from bs4 import BeautifulSoup
print("Starting Web Crawling ...")
#website to crawl
website="https://docs.arduino.cc/hardware/uno-rev3/"
#google search
#keywords = ["arduino","datasheet"]
#googlesearch = "https://www.google.com/search?client=firefox-b-d&q="
#search = "+".join(keywords)
#website = googlesearch+search
# get page
page = requests.get(website)
#extract html data
content = BeautifulSoup(page.text, 'html.parser')
# extract tags
h1_elms = content.find_all('h1')
print("Board : ",h1_elms)
#get element by class
description = content.find(class_="product-features__description").text
print("Description : ",description)
Starting Web Crawling ...
Board : [<h1>UNO R3</h1>]
Description : Arduino UNO is a microcontroller board based on the ATmega328P. It has 14 digital input/output pins (of which 6 can be used as PWM outputs), 6 analog inputs, a 16 MHz ceramic resonator, a USB connection, a power jack, an ICSP header and a reset button. It contains everything needed to support the microcontroller; simply connect it to a computer with a USB cable or power it with a AC-to-DC adapter or battery to get started. You can tinker with your UNO without worrying too much about doing something wrong, worst case scenario you can replace the chip for a few dollars and start over again.
On pourrait imaginer boucler cet opération sur une liste d’URL pour plusieurs cartes.
Avec cette méthode, on ne peut malheureusement pas charger la liste détaillé des spécification « Tech Specs » pour cela nous devons nous servir du navigateur.
Mettre en place un Web Crawler avec Selenium
Pour charger une page rien de plus facile
from selenium import webdriver
GECKOPATH = "PATH_TO_GECKO"
sys.path.append(GECKOPATH)
print("Starting Web Crawling ...")
#website to crawl
website="https://docs.arduino.cc/hardware/uno-rev3/"
#create browser handler
browser = webdriver.Firefox()
browser.get(website)
#browser.quit()
Validation des cookies
En affichant la page, vous aller certainement tomber sur la bannière de cookie qu’il faudra valider ou non pour continuer la navigation. Pour cela, il faut retrouver et cliquer sur le bouton « accepter »
Comme la page est affiché dans le navigateur, il faut un certain temps pour qu’elle charge les données et que toutes les balises soient affichées. Pour attendre le chargement, vous pouvez attendre un temps arbitraire
browser.implicitly_wait(10)
Ou attendre qu’une balise particulière soit présente comme le bouton d’acceptation des cookies
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
def waitForElement(locator, timeout ):
elm = WebDriverWait(browser, timeout).until(expected_conditions.presence_of_element_located(locator))
return elm
myElem =waitForElement((By.CLASS_NAME , 'iubenda-cs-accept-btn'),30)
N.B: Si vous rencontrez d’autre problème (élément inconnu , bouton non cliquable, etc.) dans le script alors qu’il n’y a pas de soucis sur la page Web, n’hésitez pas à utiliser la fonction time.sleep()
Chercher et appuyer sur un élément DOM
Pour afficher les spécifications techniques, le script doit cliquer sur l’onglet ‘Tech Specs’. Il faut donc trouver l’élément à partir du texte. Pour cela, il y a deux méthodes: tester le texte de l’élément ou utiliser Xpath
#get element by text
btn_text = 'Tech Specs'
btn_elms = browser.find_elements(By.CLASS_NAME,'tabs')[0].find_elements(By.TAG_NAME,'button')
for btn in btn_elms:
if btn.text == btn_text:
btn.click()
spec_btn = browser.find_element(By.XPATH, "//*[contains(text(),'Tech Specs')]")
spec_btn.click()
Récupérer les données désirées
Une fois la page souhaitée chargée, vous pouvez récupérer les données
Soit toutes les données qui sont affichées sous forme de tableau
#get all rows and children
print("Tech specs")
print("-------------------------------------")
tr_elms = browser.find_elements(By.TAG_NAME,'tr')
for tr in tr_elms:
th_elms = tr.find_elements(By.XPATH, '*')
if len(th_elms)>1:
print(th_elms[0].text, " : ", th_elms[1].text)
Starting Web Crawling ...
Page is ready!
Tech specs
-------------------------------------
Name : Arduino UNO R3
SKU : A000066
Built-in LED Pin : 13
Digital I/O Pins : 14
Analog input pins : 6
PWM pins : 6
UART : Yes
I2C : Yes
SPI : Yes
I/O Voltage : 5V
Input voltage (nominal) : 7-12V
DC Current per I/O Pin : 20 mA
Power Supply Connector : Barrel Plug
Main Processor : ATmega328P 16 MHz
USB-Serial Processor : ATmega16U2 16 MHz
ATmega328P : 2KB SRAM, 32KB FLASH, 1KB EEPROM
Weight : 25 g
Width : 53.4 mm
Length : 68.6 mm
Specific data
-------------------------------------
Main Processor : ATmega328P 16 MHz
PS D:\Formation\Python\WebCrawler>
Récupérer des données sur différentes pages
Une fois que vous maitrisez ces outils et avez une bonne idée des données à récupérer et de la structure des pages Web, vous pouvez scraper des données sur plusieurs pages. Dans ce dernier exemple, nous récupérons les données techniques de différentes cartes Arduino. Pour cela, nous créons une boucle qui va exécuter le code précédent sur une liste de site
Nous allons voir dans ce tutoriel comment créer une application de bureau avec le framework Electron. Cet environnement de programmation permet de développer des IHM à l’aide des langages Web JavaScript, HTML et CSS compatible sur plusieurs OS.
Mise en place de l’environnement de programmation
Pour ce tutoriel, nous utilisons l’éditeur de code VS Code.
Le framework Electron fonctionne avec le logiciel Node.js. Téléchargez et installez la dernière version
ou entrez les commandes suivantes sur Linux
sudo apt-get install nodejs npm
Vérifier les versions installées. Cela vous servira pour la compatibilité des librairies
node -v
npm -v
Dans le terminal de VSCode, créer votre répertoire de travail
mkdir ElectronApp && cd ElectronApp
Dans le répertoire de l’application, entrer la commande d’initialisation pour créer les fichiers par défaut
npm init
Entrez les informations demandées. Notez que Electron prend le fichier main.js en point d’entrée (entry point).
Press ^C at any time to quit.
package name: (electronapp)
version: (1.0.0)
description: custom electron app for dev and tutorials
entry point: (index.js) main.js
test command:
git repository:
keywords:
author: aranacorp
license: (ISC) MIT
About to write to D:\Formation\Javascript\Electron\ElectronApp\package.json:
{
"name": "electronapp",
"version": "1.0.0",
"description": "custom electron app for dev and tutorials",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "electron ."
},
"author": "aranacorp",
"license": "MIT"
}
Vous pouvez ensuite installer le paquet electron
npm install --save-dev electron
Une fois le fichier package.json créé, ajouter la ligne suivante dans scripts
"start": "electron ."
Ce scirpt permet de lancer l’application à l’aide la commande
npm start
Création de la première application Electron
Dans cet exemple, nous allons récupérer les versions de node et electron comme dans le tutoriel de base. Nous allons également ajouter un champ de saisie pour définir une éventuelle URL.
Pour créer l’application, nous allons créer 5 fichiers
main.js gère le cycle de vie de l’application
index.html contient la page HTML
./src/ac_style.css contient le style de la page HTML
Une fois le script lancé à l’aide de « npm start », une fenêtre s’ouvre. Vous pouvez mettre à jour l’URL à l’aide du champ de saisie et du bouton « Set ».