ENIB 2025 : Les 5 perdu : Différence entre versions
(→é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). | |
<br> | <br> | ||
− | + | Ainsi, on obtient la "masse" sur toute la ligne (tous les câbles noirs). | |
<br> | <br> | ||
</div> | </div> | ||
− | <div style="width: | + | <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
Sommaire
[masquer]Description
Nous sommes une équipe de 5 étudiants en école d'ingénieur (ENIB) :
- 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
étape 3
étape 4
étape 5
étape 6
étape 7
étape 8
étape 9
é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
- [Documentation technique de l'ESP8266 — ESP8266 Arduino Core 3.1.2-21-ga348833 documentation https://arduino-esp8266.readthedocs.io/en/latest/]
- [Bibliothèque ArduinoJson sur GitHub https://github.com/bblanchon/ArduinoJson]
- [Bibliothèque ESP8266HTTPClient sur GitHub https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPClient]
- [Installer l'ESP8266 sur Arduino https://www.wikidebrouillard.org/wiki/Utiliser_le_D1_mini_avec_Arduino]
- [Installer Arduino https://www.arduino.cc/en/software]
- [Toutes les bibliothèques disponibles de Arduino https://www.arduinolibraries.info/]
Elément de présentation
fichiers à joindre
code, ficher d'impression 3D, de découpe laser ou vinyle, ...
Mettre du code Arduino
<syntaxhighlight lang="Arduino" >
- include <ESP8266WiFi.h>
- include <WiFiClientSecure.h>
- include <ArduinoJson.h>
- include <Wire.h>
- include <Adafruit_GFX.h>
- include <Adafruit_SSD1306.h>
- include <ESP8266WebServer.h>
- include <Servo.h>
- define SCREEN_WIDTH 128 // OLED display width, in pixels
- define SCREEN_HEIGHT 32 // OLED display height, in pixels
- define OLED_ADDR 0x3C
- 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 += ""; html += ""; server.send(200, "text/html", html); });Entrée pour l'API Gemini
"; 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;
}