|
|
Ligne 137 : |
Ligne 137 : |
| ===étape 9=== | | ===étape 9=== |
| | | |
− | <div style="overflow: hidden; display: flex; align-items: flex-start; height: 270px;"> | + | <div style="overflow: hidden; display: flex; align-items: flex-start;height: 270px;"> |
− | <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>
| + | <br> |
− | Ainsi, on obtient la "masse" sur toute la ligne (tous les câbles noirs). | + | Ainsi on obtient la "masse" sur toute la ligne (tout les câbles noir). |
| <br> | | <br> |
| </div> | | </div> |
− | <div style="width: 50%; margin-left: 10px; text-align: right; display: block;"> | + | <div style="width: 40%; margin-left: 10px; text-align: right;""> |
− | <gallery mode="packed-hover" heights=200 widths=200 style="margin-left: auto;"> | + | <gallery mode="packed-hover" heights=200 widths=200 right> |
− | File:Multipliergnd2.jpg|200px|''Câblage de la masse'' | + | File:Multipliergnd2.jpg|200px|''cablage de la masse'' |
− | File:Multipliergnd3.jpg|200px|''Schéma de la masse'' | + | File:Multipliergnd3.jpg|200px|''schéma de la masse'' |
| </gallery> | | </gallery> |
| </div> | | </div> |
Ligne 214 : |
Ligne 214 : |
| * [Installer Arduino https://www.arduino.cc/en/software] | | * [Installer Arduino https://www.arduino.cc/en/software] |
| * [Toutes les bibliothèques disponibles de Arduino https://www.arduinolibraries.info/] | | * [Toutes les bibliothèques disponibles de Arduino https://www.arduinolibraries.info/] |
| + | * [api gemini google cloud https://ai.google.dev/gemini-api/docs/api-key?hl=fr] |
| + | * [open WeatherMap https://ai.google.dev/gemini-api/docs/api-key?hl=fr] |
| + | |
| + | |
| | | |
| | | |
Ligne 223 : |
Ligne 227 : |
| ===Mettre du code Arduino=== | | ===Mettre du code Arduino=== |
| | | |
− | <syntaxhighlight lang="Arduino" >
| + | * [lien github du code complet https://github.com/FoxtrotFR/TortueGPT] |
− | #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><body>";
| |
− | html += "<h1>Entrée pour l'API Gemini</h1>";
| |
− | html += "<form action='/submit' method='POST'>";
| |
− | html += "<input type='text' name='message' placeholder='Entrez votre message' />";
| |
− | html += "<input type='submit' value='Envoyer' />";
| |
− | html += "</form>";
| |
− | html += "</body></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", "<html><body><h1>Message envoyé!</h1></body></html>");
| |
− | } else {
| |
− | server.send(400, "text/html", "<html><body><h1>Erreur: Aucune donnée reçue</h1></body></html>");
| |
− | }
| |
− | });
| |
− | | |
− | // 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== | | ==ne pas modifier sous cette ligne== |