C'est le printemps ! Fleur.gif

ENIB 2025 : Les 5 perdu : Différence entre versions

De Les Fabriques du Ponant
Aller à : navigation, rechercher
(étape 9)
(étape 9)
Ligne 140 : Ligne 140 :
 
   <div style="width: 50%; text-align: justify; justify-content: center;">
 
   <div style="width: 50%; text-align: justify; justify-content: center;">
 
     <br>
 
     <br>
    Comme nous avons besoin de la masse sur plusieurs branchements, nous devons la "multiplier". Donc on branche un câble venant de la pin G de la carte sur une ligne de notre plaque Labtec (voir photo).
+
Comme nous avons besoin de la masse sur plusieurs branchements, nous devons la "multiplier". Donc on branche un câble venant de la pin G de la carte sur une ligne de notre plaque Labtec (voir photo).
 
     <br>
 
     <br>
    Ainsi, on obtient la "masse" sur toute la ligne (tous les câbles noirs).
+
Ainsi, on obtient la "masse" sur toute la ligne (tous les câbles noirs).
 
     <br>
 
     <br>
 
   </div>
 
   </div>
   <div style="width: 40%; margin-left: 10px; text-align: right; display: block;">
+
   <div style="width: 50%; margin-left: 10px; text-align: right; display: block;">
 
     <gallery mode="packed-hover" heights=200 widths=200 style="margin-left: auto;">
 
     <gallery mode="packed-hover" heights=200 widths=200 style="margin-left: auto;">
 
       File:Multipliergnd2.jpg|200px|''Câblage de la masse''
 
       File:Multipliergnd2.jpg|200px|''Câblage de la masse''

Version du 23 janvier 2025 à 17:14

La Tortue GPT


Description

Nous sommes une équipe de 5 étudiants en école d'ingénieur (ENIB) :

Photo de groupe les 5 perdu.jpg

  • Maxime LE GAC (pull noir)
  • Tudual DUPUY (pull blanc)
  • Loan ANDRO (pull gris)
  • Clément BERTONCINI (pull bordeaux)
  • Alexandre LE POULICHET (pull bleu)

Introduction

Notre objectif durant ces deux jours est de concevoir une tortue capable de répondre à nos questions par oui ou par non à l'aide d'une intelligence artificielle.

Outil et matériel

  • 1 carte D1 mini avec wifi
  • 2 led
  • 2 résistances de 220 Ω
  • 1 plaque labtec
  • fils de connexion
  • carton

Etapes de fabrication

étape 1

Lors de cette étape, nous réalisons les patrons pour confectionner et assembler la tortue (cliquez sur la photo 1 pour agrandir le modèle de base). Ensuite, nous découpons les patrons en carton pour les pattes et le ventre de la tortue, puis nous pouvons les assembler (photos 2 et 3).

Une fois les pattes et le ventre terminés, vous pouvez découper le patron de la tête de la tortue et faire un trou d'environ 5mm de diamètre pour laisser passer le bout de plastique (comme illustré sur la photo 4).

Ensuite, vous pouvez découper le ventre de la tortue et coller la plaque Labtec ainsi que le cerveau moteur, comme montré sur la photo 5.

étape 2

Maintenant, vous pouvez réaliser le circuit comme indiqué ci-contre sur le schéma de la plaque Labtec. Nous utilisons des résistances de 220 ohms pour éviter de court-circuiter les LED (c'est une sécurité). N'oubliez pas de faire des trous pour laisser passer les câbles qui alimenteront les LED. Vous devez aussi faire un trou comme sur la photo 1 pour mettre l'écran (vous pouvez choisir l'emplacement).

étape 3

Légende de l'image

Avant de refermer la tête de la tortue, vous pouvez faire un trou d'environ 3mm de diamètre dans le carton pour les LED et coller les câbles Male-Female aux LED à l'aide de colle chaude, comme indiqué sur le circuit. (Attention, la tige la plus grande est le + donc à ne pas relier à la masse, le fil noir.)

Bonus : vous pouvez colorier des morceaux de papier en vert et les coller sur la tête.

étape 4

Légende de l'image

Une fois que vous avez fini toutes les étapes précédentes, vous pouvez relier la tête et le corps de la tortue à l'aide d'une attache ou d'une charnière pour que cette dernière puisse tourner la tête. (N'oubliez pas de faire un trou dans la tête pour laisser passer les câbles qui allument les LED.)

étape 5

Légende de l'image

Vous pouvez coller le ventre de la tortue à la carapace à l'aide d'un pistolet à colle chaude basse température (voir photo résultat). Veillez à bien aligner les deux parties avant de coller pour un résultat esthétique. (Notez que le ventre est important, car il empêche la tête de toucher le sol.)

étape 6

Maintenant, tu peux réaliser le patron et, une fois découpé, tu peux le décorer. Pour cela, découpe des morceaux de papier et colle-les sur le carton à l'aide d'un bâton de colle. Tu vas devoir adapter les dimensions des patrons précédents et les colorier comme tu le souhaites !

étape 7

Légende de l'image

Une fois que vous avez fini de décorer les pattes, vous pouvez les coller avec un pistolet à colle sur le ventre de la tortue en respectant la symétrie. (Maintenant votre tortue est presque prête !!!)

étape 8

Légende de l'image

Une fois que vous avez fini de décorer les pattes, vous pouvez les coller avec un pistolet à colle sur le ventre de la tortue en respectant la symétrie. (Maintenant votre tortue est presque prête !!!)

étape 9

   

Comme nous avons besoin de la masse sur plusieurs branchements, nous devons la "multiplier". Donc on branche un câble venant de la pin G de la carte sur une ligne de notre plaque Labtec (voir photo).

   

Ainsi, on obtient la "masse" sur toute la ligne (tous les câbles noirs).

   

étape 10


Voilà quelques photo pour vous aider à faire les branchements

Pour le cerveau moteur -carte
controle-5V
masse-G
VCC-5

Pour l'écran-carte
GND-G
VCC-3.3
SCL-D1
SDA-D2

Pour la led - carte
borne + - D1 (LED1)
borne+ - D2 (LED2)

étape 11

C'est parti pour la programmation ! Première étape : installez Arduino IDE puis les bibliothèques requises (vous les trouverez facilement dans la section "Sources"). Assurez vous bien d'avoir toutes les bibliothèques qui se trouvent au début du code ce sont les #include. Le code source est prêt à l'emploi, avec toutes les bibliothèques incluses. Vérifiez bien que votre installation correspond au code. N'hésitez pas à explorer le code et à vous appuyer sur la documentation de l'ESP8266 pour approfondir et toutes les sources disponibles.



Troubleshouting

Quelles sont difficultés, les problèmes, quelles sont les solutions, les trucs et astuces pour que ça marche ?

    Nous avons rencontré de nombreux problèmes, notamment au niveau du code :
  • 1. Gestion des librairies Arduino : • Difficultés à intégrer correctement toutes les bibliothèques nécessaires dans le code.
  • 2. Connexion au réseau Wi-Fi : • Configuration de l’identifiant et des paramètres du Wi-Fi pour assurer une connexion stable à l’API.
  • 3. Lecture des requêtes JSON : • Problèmes rencontrés lors de l’analyse et du traitement des données JSON issues des requêtes.
  • 4. Filtrage des réponses : • Challenge lié à l’affichage de la réponse souhaitée. • Nous recevions beaucoup d’informations inutiles sur le moniteur série, car l’intégralité de la requête JSON s’affichait, rendant l’extraction des données pertinentes plus complexe.

Sources et documentation complémentaire


Elément de présentation

La Tortue GPT.jpg

fichiers à joindre

code, ficher d'impression 3D, de découpe laser ou vinyle, ...

Mettre du code Arduino

<syntaxhighlight lang="Arduino" >

  1. include <ESP8266WiFi.h>
  2. include <WiFiClientSecure.h>
  3. include <ArduinoJson.h>
  4. include <Wire.h>
  5. include <Adafruit_GFX.h>
  6. include <Adafruit_SSD1306.h>
  7. include <ESP8266WebServer.h>
  8. include <Servo.h>
  1. define SCREEN_WIDTH 128 // OLED display width, in pixels
  2. define SCREEN_HEIGHT 32 // OLED display height, in pixels
  3. define OLED_ADDR 0x3C
  4. define PIN_SERVO_2 D5

Servo myservo_2;

// Informations Wi-Fi const char* ssid = "Fablab 2.4"; const char* password = "MonPetitPonant";

// Serveur Gemini const char* host = "generativelanguage.googleapis.com"; const char* whost = "api.openweathermap.org"; const int httpsPort = 443; int i = 0;

// Clé API Gemini const char* apiKey = "AIzaSyAoicxEwA1SBs5j0TOFz6k1Je9tjHcCDv8"; const char* wapiKey = "9596a6bc06ebb5c5671dd0d0cf21a71e";

// Coordonnées de Brest, France const float latitude = 48.3904; const float longitude = -4.4861;

// Variable pour suivre la dernière mise à jour unsigned long lastWeatherUpdate = 0; const unsigned long weatherUpdateInterval = 30 * 60 * 1000; // 30 minutes en millisecondes

// URL pour accéder à la météo String url = "/data/2.5/weather?lat=" + String(latitude, 4) + "&lon=" + String(longitude, 4) + "&appid=" + wapiKey + "&units=metric&lang=fr";

//Serveur Web Inclus ESP8266WebServer server(80); // Serveur web sur le port 80

// Information Ecran Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


void setup() {

WiFiClientSecure client;
client.setInsecure(); // Désactive la vérification du certificat SSL
Serial.begin(9600);
delay(10000);


// Connexion au Wi-Fi
Serial.print("Connexion au Wi-Fi");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi connecte.");
Serial.print("IP du Device : ");
Serial.println(WiFi.localIP()); // Afficher l'adresse IP locale
// Page HTML avec un champ de texte pour l'entrée utilisateur
server.on("/", HTTP_GET, []() {
String html = "";
 html += "

Entrée pour l'API Gemini

"; html += "
"; html += ""; html += ""; html += "
"; html += ""; server.send(200, "text/html", html); });
// Lorsque le formulaire est soumis, traiter la requête
server.on("/submit", HTTP_POST, []() {
if (server.hasArg("message")) {
String userMessage = server.arg("message"); // Récupérer le message
Serial.println("Message reçu: " + userMessage);

// Appeler votre fonction pour envoyer la requête à l'API
sendRequestToGemini(userMessage);
// Afficher une page de confirmation
server.send(200, "text/html", "

Message envoyé!

"); } else { server.send(400, "text/html", "

Erreur: Aucune donnée reçue

"); } });
// Démarrer le serveur web
server.begin();
// Initialiser l'écran
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println(F("Échec de l'initialisation de l'écran SSD1306!"));
for (;
}
// Affichage de l'adresse IP locale pendant 10 secondes sur l'écran OLED
display.clearDisplay();
display.setTextSize(1); // Taille de texte 1
display.setTextColor(SSD1306_WHITE); // Couleur du texte
display.setCursor(0, 0); // Positionner le curseur en haut à gauche
display.print("IP locale: ");
display.print(WiFi.localIP()); // Afficher l'adresse IP
display.display(); // Mettre à jour l'écran
// Effacer l'écran
display.clearDisplay();
delay(10000); // Attendre 10 secondes
// Configurer la taille du texte et la couleur
display.setTextSize(1); // Taille du texte (1 = normal, 2 = double, etc.)
display.setTextColor(SSD1306_WHITE); // Couleur du texte
// Test liaison Meteo
Serial.print("Connexion au serveur Meteo...");
if (!client.connect(whost, httpsPort)) {
Serial.println("Échec de la connexion au serveur Meteo.");
return;
}
Serial.println("Connecté au serveur Meteo.");
// Test Liaison Gemini
Serial.print("Connexion au serveur Gemini...");
if (!client.connect(host, httpsPort)) {
Serial.println("Échec de la connexion au serveur Gemini.");
return;
}
Serial.println("Connecté au serveur Gemini.");
pinMode(D4, OUTPUT);
pinMode(D3, OUTPUT);
myservo_2.attach(PIN_SERVO_2);

}

void loop() {

server.handleClient(); // Traiter les requêtes entrantes
// Vérifie si 30 minutes se sont écoulées depuis la dernière mise à jour
while (i == 0) {
fetchWeather(url, whost);
i++;
}
if (millis() - lastWeatherUpdate >= weatherUpdateInterval) {
// Mettre à jour la météo
fetchWeather(url, whost);
// Met à jour le timestamp de la dernière mise à jour
lastWeatherUpdate = millis();
}
// Entrer Gemini Manuel en direct via le Moniteur de serie
// Demande à l'utilisateur d'entrer un message
//Serial.println("Entrez votre message pour l'API Gemini :");
// Attente de l'entrée utilisateur
//while (Serial.available() == 0) {
// Attente active
//}
// Lecture du message saisi
//String userMessage = Serial.readStringUntil('\n');
// Appel de la fonction pour envoyer la requête
//sendRequestToGemini(userMessage);
//delay(10000); // Attendre 10 secondes avant d'envoyer une nouvelle requête

} String replaceAccents(String str) {

str.replace("é", "e");
str.replace("è", "e");
str.replace("à", "a");
str.replace("ù", "u");
str.replace("°", " ");
str.replace("*", " ");
// Ajoutez d'autres remplacements nécessaires
return str;

} const char* json(String input) {

DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, input);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return 0;
}
JsonObject candidates_0 = doc["candidates"][0];
const char* candidates_0_content_parts_0_text = candidates_0["content"]["parts"][0]["text"]; // "Bonjour ...
return candidates_0_content_parts_0_text;

} void sendRequestToGemini(const String& message) {

Serial.println("Gemini Mode");
WiFiClientSecure client;
client.setInsecure(); // Désactive la vérification du certificat SSL
Serial.print("Connexion au serveur...");
if (!client.connect(host, httpsPort)) {
Serial.println("Échec de la connexion au serveur.");
return;
}
Serial.println("Connecté au serveur.");
String instruction = " pour ta reponse : pas d'emoticone, pas de symbole, uniquement des lettres, chiffres... et la ponctuation";
String finalmessage = message + finalmessage;
// Construire le payload JSON
String payload = String("{") +
"\"contents\": [{" +
"\"parts\": [{\"text\": \"" + finalmessage + "\"}]" +
"}]" +
"}";
// En-têtes HTTP
String headers = String("POST /v1beta/models/gemini-1.5-flash:generateContent?key=") + apiKey + " HTTP/1.1\r\n" +
"Host: " + String(host) + "\r\n" +
"Content-Type: application/json\r\n" +
"Content-Length: " + String(payload.length()) + "\r\n" +
"\r\n";
// Envoi de la requête
client.print(headers + payload);
Serial.println("Requête envoyée.");
BougerRobot();
// Lecture de la réponse
String response = "";
while (client.connected() || client.available()) {
String line = client.readStringUntil('\n');
if (line == "\r") break; // Fin des en-têtes
}
// Lire le corps de la réponse
while (client.available()) {
response += client.readString();
}
client.stop();
// Afficher la réponse brute
//Serial.println("Réponse brute de l'API :");
String modified = response.substring(5, response.length() - 8);
//Serial.println(modified);
String messagestr = json(modified);
String realmessage = replaceAccents(messagestr);
Serial.println(realmessage);
// Calculer la hauteur totale du texte
int lineHeight = 8; // Hauteur d'une ligne de texte
int numLines = (realmessage.length() / (SCREEN_WIDTH / 6)) + 1;
int textHeight = numLines * lineHeight;
if (textHeight <= SCREEN_HEIGHT) {
// Le texte tient dans l'écran, affichage direct
display.clearDisplay();
display.setCursor(0, 0); // Positionner le curseur en haut à gauche
display.println(realmessage); // Afficher la variable
display.display(); // Mettre à jour l'écran
} else {
// Le texte dépasse, activer le défilement
int scrollOffset = 0;
while (scrollOffset <= textHeight) {
display.clearDisplay(); // Effacer l'écran
display.setCursor(0, -scrollOffset); // Ajuster le décalage vertical
display.println(realmessage); // Dessiner le texte
display.display(); // Mettre à jour l'écran
scrollOffset += 1; // Incrémenter le décalage
delay(100); // Ajuster la vitesse du défilement
}
// Réinitialiser l'écran après défilement
display.clearDisplay();
display.setCursor(0, 0);
display.println("Defilement termine."); // Affiche un message de fin, si nécessaire
display.display();
}
delay(2000);
fetchWeather(url, whost);

}

void fetchWeather(String url, const char* whost) {

WiFiClientSecure client;
client.setInsecure(); // Désactive la vérification du certificat SSL
Serial.print("Connexion au serveur Meteo...");
if (!client.connect(whost, httpsPort)) {
Serial.println("Échec de la connexion au serveur Meteo.");
return;
}
// Envoi de la requête HTTP GET
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + whost + "\r\n" +
"Connection: close\r\n\r\n");
// Lecture de la réponse
String response = "";
while (client.connected() || client.available()) {
String line = client.readStringUntil('\n');
if (line == "\r") break; // Fin des en-têtes
}
while (client.available()) {
response += client.readString();
}
client.stop();
// Analyse de la réponse JSON
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, response);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// Extraction des données
const char* weatherDescription = doc["weather"][0]["description"];
float temperature = doc["main"]["temp"];
const char* icon = doc["weather"][0]["icon"];
// Affichage des données sur l'écran OLED
display.clearDisplay();
display.setCursor(0, 0);
display.print("Meteo a Brest:");
display.setCursor(0, 10);
display.print(weatherDescription);
display.setCursor(0, 20);
display.print(temperature);
display.print(" C");
// Affichage de l'icône correspondante
display.setCursor(100, 0);
if (strstr(weatherDescription, "ensoleille") != nullptr || strstr(weatherDescription, "clair") != nullptr) {
display.fillCircle(110, 10, 5, SSD1306_WHITE);
} else if (strstr(weatherDescription, "nuage") != nullptr || strstr(weatherDescription, "couvert") != nullptr) {
display.fillRect(105, 5, 15, 10, SSD1306_WHITE);
} else {
// Afficher une icône par défaut
display.fillRect(105, 5, 15, 10, SSD1306_WHITE);
}
display.display();

} void BougerRobot() {

int compteur = 0;
while (compteur < 3) {
digitalWrite(D4, HIGH);
digitalWrite(D3, HIGH);
myservo_2.write(0);
delay(1000*1);
digitalWrite(D4, LOW);
digitalWrite(D3, LOW);
myservo_2.write(180);
delay(2000*1);
compteur++;
}
compteur = 0;

}

ne pas modifier sous cette ligne