« I2L 2024 Groupe3 » : différence entre les versions
Ligne 89 : | Ligne 89 : | ||
= Montage = | = Montage = | ||
[[Fichier:20250427 161907.jpg|gauche|vignette|390x390px|Montage final avec affichage front sur le PC]] | |||
= Code Backend = | = Code Backend = |
Version du 27 avril 2025 à 14:33
Proposition de système
Création d'un sonar 180 degrés qui écrit des données rudimentaires sur un écran LCD et qui, lorsqu'on connecte le microcontrôleur au PC, envoi en temps réel les données au PC et génère un affichage style sonar.
On a 2 programmes :
- 1 programme qui tourne dans le microcontrôleur, il est autonome, fait tourner a l'aide d'un servo-moteur le sonar (capteur style sonar) et affiche des informationss rudimentaires sur les obstacles repérés (e.g. l'obstacle le plus proche) ;
- 1 programme qui tourne sur le PC connecté au microcontrôleur. Il récupère les données en temps réel envoyées par le microcontrôleur et affiche graphiquement le résultat ;
- 2 LED de couleur (vert et rouge), la LED verte clignote toute les 3 secondes quand le système embarqué est autonome et la LED rouge, éteinte par défaut, double clignote quand un obstacle est détecté.
A prévoir un bouton "autonomous_system" sur le microcontroleur, qui, lorsque celui-ci est branché au PC, n'envoie pas les données au PC.
Contre-proposition
OK pour la proposition.
Pour la gestion de l'écran vous pouvez vous inspirer du code des groupes 3 et 4 I2L en 2023/2024.
Vous utiliserez la classe USB "vendeur spécifique" avec des points d'accès propres à votre application. Plus exactement vous prévoierez un point d'accès sortant pour spécifier l'angle du servo-moteur et un point d'accès entrant pour récupérer la distance mesurée.
Pour l'application sur PC vous utiliserez la bibliothèque C libusb-1.0.
Il faut aussi penser à un système pour assembler le servo-moteur et le sonar. Si vous avez accès à une imprimante 3D suivez ce lien [1].
Proposition définitive
Le projet consiste en la création d’un sonar à balayage 180° capable de détecter des obstacles et d’afficher les données recueillies de deux façons complémentaires :
- Un premier programme est embarqué dans le microcontrôleur. Il pilote un capteur à ultrasons monté sur un servomoteur afin de balayer un angle de 180°. Ce programme fonctionne de manière autonome et affiche des informations rudimentaires sur un écran LCD, comme la distance de l'obstacle le plus proche détecté.
- Un second programme, exécuté sur un ordinateur connecté au microcontrôleur, reçoit en temps réel les données envoyées par celui-ci via USB. Il se charge de les représenter graphiquement sous forme d’un affichage de type sonar 180°, facilitant la visualisation des obstacles détectés dans l’environnement.
Répartition du travail
La répartition des tâches au sein du groupe s’est organisée de la manière suivante :
- Thomas a pris en charge la gestion de l’écran LCD ainsi que le contrôle du servomoteur.
- Adrien s’est occupé de l’architecture générale du code et du pilotage du sonar.
- Mathieu a travaillé sur l’intégration de LUFA (USB), une tâche complexe ayant nécessité plusieurs ajustements.
Par ailleurs, de nombreuses sessions de pair programming ont été menées, notamment lors de l’apparition de problèmes liés à la LUFA.
Carte
Schéma initial
- schéma : Fichier:I2L-2024-Carte-G3a.zip
Carte routée
Composants
- ATmega32u4 : disponible
- quartz GND24 : disponible
- buzzer : disponible
- perle ferrite MH2029-300Y : commandée
- chargeur MAX1811 : disponible
- potentiomètre : disponible
- connecteur femelle 16 contacts : commandé
- écran LCD 2 lignes : commandé
- boutons : disponibles
- sonar HC-SR4 : disponible
- servo-moteur 270° : disponible
Carte au 23/02/2025
Non encore réalisé :
- ajouter la perle de ferrite ;
- ajouter les connecteurs J5, J6, J7 et J9 pour la charge ;
- ajouter le condensateur de 2,2uF pour la charge ;
- ajouter R8 et le potentiomètre pour l'écran LCD ;
- ajouter le buzzer ;
- ajouter M1 et J3 pour le servo-moteur et le sonar.
- remplacer une led rouge par une led verte.
Carte au 27/02/2025
Non encore réalisé :
- ajouter la perle de ferrite ;
- remplacer une led rouge par une led verte.
Carte au 07/03/2025
Une erreur a été introduite au moment du routage de la carte : la ligne de commande du serveur a été mise sur PF6 qui ne peut pas être commandée en PWM par un compteur. Du coup, des fils sont soudés à la place du buzzer pour commander le servo avec PB7.
Montage
Code Backend
firmware/HD44780.c
Communication avec un écran LCD HD44780 en mode 4 bits. Initialise l'écran, envoie des commandes, écrit du texte ou affiche des nombres correspondant a la distance d'un obstacle et son angle.
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include <string.h>
#include "HD44780_private.h"
#include "HD44780.h"
static void HD44780_WriteNibble(const uint8_t nib)
{
LCD_EN_PORT &= ~(1<<LCD_EN_PIN);
if(nib & 0x08) LCD_D7_PORT |= (1<<LCD_D7_PIN);
else LCD_D7_PORT &= ~(1<<LCD_D7_PIN);
if(nib & 0x04) LCD_D6_PORT |= (1<<LCD_D6_PIN);
else LCD_D6_PORT &= ~(1<<LCD_D6_PIN);
if(nib & 0x02) LCD_D5_PORT |= (1<<LCD_D5_PIN);
else LCD_D5_PORT &= ~(1<<LCD_D5_PIN);
if(nib & 0x01) LCD_D4_PORT |= (1<<LCD_D4_PIN);
else LCD_D4_PORT &= ~(1<<LCD_D4_PIN);
_delay_us(1);
LCD_EN_PORT |= (1<<LCD_EN_PIN);
_delay_us(1);
LCD_EN_PORT &= ~(1<<LCD_EN_PIN);
_delay_us(100);
}
static void HD44780_WriteByte(const uint8_t c)
{
HD44780_WriteNibble(c >> 4);
HD44780_WriteNibble(c & 0x0F);
}
static void HD44780_PowerUp4Bit(void)
{
_delay_ms(40);
HD44780_WriteNibble(0x03);
_delay_ms(5);
HD44780_WriteNibble(0x03);
_delay_us(100);
HD44780_WriteNibble(0x03);
_delay_us(50);
HD44780_WriteNibble(0x02);
_delay_us(50);
}
void HD44780_Initialize(void)
{
LCD_D4_DDR |= (1<<LCD_D4_PIN);
LCD_D5_DDR |= (1<<LCD_D5_PIN);
LCD_D6_DDR |= (1<<LCD_D6_PIN);
LCD_D7_DDR |= (1<<LCD_D7_PIN);
LCD_RS_DDR |= (1<<LCD_RS_PIN);
LCD_RW_DDR |= (1<<LCD_RW_PIN);
LCD_EN_DDR |= (1<<LCD_EN_PIN);
LCD_D4_PORT &= ~(1<<LCD_D4_PIN);
LCD_D5_PORT &= ~(1<<LCD_D5_PIN);
LCD_D6_PORT &= ~(1<<LCD_D6_PIN);
LCD_D7_PORT &= ~(1<<LCD_D7_PIN);
LCD_RS_PORT &= ~(1<<LCD_RS_PIN);
LCD_RW_PORT &= ~(1<<LCD_RW_PIN);
LCD_EN_PORT &= ~(1<<LCD_EN_PIN);
HD44780_PowerUp4Bit();
_delay_ms(50);
}
void HD44780_WriteCommand(const uint8_t c)
{
LCD_RS_PORT &= ~(1<<LCD_RS_PIN);
HD44780_WriteByte(c);
_delay_us(50);
}
void HD44780_WriteData(const uint8_t c)
{
LCD_RS_PORT |= (1<<LCD_RS_PIN);
HD44780_WriteByte(c);
LCD_RS_PORT &= ~(1<<LCD_RS_PIN);
_delay_us(50);
}
void HD44780_WriteString(char *string)
{
for (int i = 0; i < strlen(string); i++)
HD44780_WriteData(string[i]);
}
void HD44780_WriteInteger(int num, int radix)
{
char s[MAX_DIGITS];
itoa(num, s, radix);
HD44780_WriteString(s);
}
int HD44780_XY2Adrr(int nbrows, int nbcols, int row, int col)
{
int row_offsets[] = {0x00, 0x40, 0x14, 0x54};
if (row >= nbrows) row = nbrows - 1;
if (col >= nbcols) col = nbcols - 1;
return row_offsets[row] + col;
}
Code Front
pc_app/SonarFront.py
import serial
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import subprocess
import os
import select
# Mode d'utilisation d'un périphérique virtuel (True) ou réel (False)
VIRTUAL_DEVICE_MODE = False
# Initialisation du périphérique virtuel si nécessaire
if VIRTUAL_DEVICE_MODE:
VIRTUAL_PORT = '/tmp/virtual_serial'
if not os.path.exists(VIRTUAL_PORT):
os.mkfifo(VIRTUAL_PORT)
# Lance le script qui simule un périphérique série
sim_process = subprocess.Popen(['/bin/bash', './pc_app/simulate_serial.sh'])
def read_coordonnees():
"""
Lit une ligne de données depuis le port série ou virtuel,
la décode, puis retourne un tuple (rayon, angle) en float.
Retourne (0.0, 0.0) en cas d'erreur ou d'absence de données.
"""
try:
if VIRTUAL_DEVICE_MODE:
with open(VIRTUAL_PORT, 'r') as f:
rlist, _, _ = select.select([f], [], [], 0.1)
if rlist:
line = f.readline().strip()
else:
return (0.0, 0.0)
else:
with serial.Serial('/dev/ttyUSB0', 9600, timeout=1) as ser:
line = ser.readline().decode('utf-8').strip()
if line:
parts = line.split(',')
if len(parts) == 2:
return (float(parts[0]), float(parts[1]))
return (0.0, 0.0)
except Exception as e:
print(f"Erreur de lecture: {str(e)}")
return (0.0, 0.0)
# Configuration de la fenêtre graphique
fig = plt.figure(figsize=(14, 7))
ax = fig.add_subplot(111, projection='polar') # Graphique en coordonnées polaires
ax.set_ylim(0, 5.5)
ax.set_thetamin(0)
ax.set_thetamax(180)
ax.set_theta_offset(np.pi) # Décalage de 180°
ax.set_theta_direction(-1) # Sens horaire
scatter = ax.scatter([], [], color='#FF9F1C', s=120, edgecolor='#EB5E28')
# Dictionnaire pour stocker les points (clé = angle, valeur = rayon)
points_dict = {}
def update(frame):
"""
Fonction appelée périodiquement par FuncAnimation.
Elle lit de nouvelles coordonnées, met à jour les points existants,
et rafraîchit l'affichage du graphique.
"""
global points_dict
r, theta = read_coordonnees()
# Ignorer les coordonnées (0,0) (pas de donnée)
if abs(r) < 1e-3 and abs(theta) < 1e-3:
pass
else:
# Ajouter ou mettre à jour un point
if theta in points_dict:
if abs(r) < 1e-3:
del points_dict[theta] # Supprimer le point si le rayon est nul
else:
points_dict[theta] = r # Mettre à jour le rayon existant
else:
if abs(r) >= 1e-3:
points_dict[theta] = r # Ajouter un nouveau point
# Préparer les données pour le graphique
data = []
for angle_deg, radius in points_dict.items():
angle_rad = np.deg2rad(angle_deg)
data.append([angle_rad, radius])
# Mise à jour de l'affichage
if data:
scatter.set_offsets(np.array(data))
else:
scatter.set_offsets(np.empty((0, 2)))
return scatter,
# Lancement de l'animation avec mise à jour toutes les 10 ms
ani = FuncAnimation(fig, update, interval=10, blit=True)
try:
plt.show()
finally:
# Nettoyage du processus de simulation et suppression du tube
if VIRTUAL_DEVICE_MODE:
sim_process.terminate()
try:
os.remove(VIRTUAL_PORT)
except:
pass
simulate_serial.sh
#!/bin/bash
# simulate_serial.sh - Simule un balayage angulaire avec distance aléatoire
angle=0.0 # Angle de départ
direction=1 # Direction d'incrémentation de l'angle
pipe="/tmp/virtual_serial" # Chemin du tube nommé pour la communication
# Forcer l'utilisation du point comme séparateur décimal
export LC_NUMERIC=C
# Créer le tube nommé si besoin
[ ! -p "$pipe" ] && mkfifo "$pipe"
# Boucle infinie de génération de données
while true; do
# Générer une distance aléatoire entre 0.50 m et 5.50 m
distance=$(echo "scale=2; $RANDOM/32767*5+0.5" | bc -l)
# Envoyer les données sous forme "distance,angle" dans le tube
printf "%s,%.2f\n" $distance $angle > "$pipe"
# Incrémenter ou décrémenter l'angle
angle=$(echo "scale=2; $angle + $direction" | bc -l)
# Inverser la direction à 0° et 180°
if (( $(echo "$angle >= 180.0" | bc -l) )); then
direction="-1"
elif (( $(echo "$angle <= 0.0" | bc -l) )); then
direction="1"
fi
# Attendre 10 ms avant d'envoyer la prochaine donnée
sleep 0.01
done
Lancement
Pour exécuter le programme sur la carte, il faut tout d’abord le compiler et le téléverser. Voici les étapes à suivre dans le terminal :
make clean # Nettoie les fichiers de compilation précédents
make # Compile le programme
reset # Réinitialise la carte (via bouton physique sur la carte)
make upload # Téléverse le programme sur la carte
Une fois ces commandes exécutées, la carte est prête à faire fonctionner le programme.
Démonstrations
Voici une vidéo qui présente le fonctionnement du sonar, ainsi que le rôle du moteur et de l’écran LCD.
Rendus
Projet KiCAD : Fichier:I2L-2024-Carte-G3.zip
Programmes :
- démonstration : Fichier:I2L-2024-Programmes-G3-rex.zip
- SIMON : Fichier:I2L-2024-Programmes-G3.zip