ENIB 2026 : La roue de la météo : Différence entre versions
(→fichiers à joindre) |
|||
| (37 révisions intermédiaires par le même utilisateur non affichées) | |||
| Ligne 1 : | Ligne 1 : | ||
| − | Titre de la fiche expérience : | + | Titre de la fiche expérience : La roue de la météo – Objet connecté ludique |
==description (résumé)== | ==description (résumé)== | ||
| − | |||
| − | + | La Roue de la météo est un objet connecté ludique permettant d’afficher la météo d’une ville de manière visuelle et interactive. | |
| − | [[Fichier: | + | L’utilisateur sélectionne une ville via un bouton physique, la carte ESP32 récupère les données météorologiques en ligne et allume une LED correspondant au type de météo. Ensuite un moteur pas à pas va faire tourner un disque sur le dessus de la boîte jusqu'au symbole météo correspondant. |
| + | |||
| + | [[Fichier:Equipeequipe.jpg|400px]] | ||
==Introduction== | ==Introduction== | ||
| − | + | Dans le cadre du hackathon ENIB 2026 avec les petits débrouillards, l’objectif est de concevoir un objet connecté à la fois simple, utile et ludique. | |
| + | Notre projet s’inscrit dans cette démarche en proposant une approche ludique pour savoir la météo d'une ville. | ||
| + | |||
| + | La Roue de la météo privilégie une interaction physique et intuitive, sans écran, afin de rendre l’information météo accessible et amusante. Le projet combine électronique, programmation, design et fabrication d’un objet. | ||
| + | |||
==outil et matériel== | ==outil et matériel== | ||
| + | |||
| + | Électronique : | ||
| + | |||
| + | Carte ESP32-S ; | ||
| + | LEDs (1 par symbole météo) ; | ||
| + | Résistances pour LEDs ; | ||
| + | Boutons poussoirs colorés (1 par ville) ; | ||
| + | Câbles de connexion ; | ||
| + | Alimentation (USB) | ||
| + | |||
| + | Fabrication : | ||
| + | |||
| + | Carton (prototype) ; | ||
| + | Bois (version finale prévue) ; | ||
| + | Cutter / ciseaux ; | ||
| + | Colle / adhésif ; | ||
| + | Support circulaire rotatif avec des symbole météo (découpeuse laser) | ||
| + | |||
| + | Logiciel : | ||
| + | |||
| + | Arduino ; | ||
| + | Bibliothèques ESP32 ; | ||
| + | API météo (OpenMétéo) | ||
==fichiers à joindre== | ==fichiers à joindre== | ||
code, ficher d'impression 3D, de découpe laser ou vinyle, ... | code, ficher d'impression 3D, de découpe laser ou vinyle, ... | ||
| + | |||
===Mettre du code Arduino=== | ===Mettre du code Arduino=== | ||
| + | |||
| + | Modifier les lignes 31 et 32 pour connecter un wifi ou un partage de connexion et faire fonctionner le lien entre le projet et l'API | ||
| + | |||
<syntaxhighlight lang="Arduino" line> | <syntaxhighlight lang="Arduino" line> | ||
| − | #define | + | #include <Stepper.h> |
| − | #include | + | |
| + | const int stepsTotalRevolution = 2048; // tour complet | ||
| + | const int stepsEighthRevolution = 256; // 1/8 de tour | ||
| + | |||
| + | // ULN2003 Motor Driver Pins | ||
| + | #define IN1 26 | ||
| + | #define IN2 27 | ||
| + | #define IN3 14 | ||
| + | #define IN4 12 | ||
| + | |||
| + | // Buttons Pins | ||
| + | #define BP_Bleu 16 | ||
| + | #define BP_Vert 17 | ||
| + | #define BP_Jaune 19 | ||
| + | #define BP_Rose 34 | ||
| + | |||
| + | bool BP_Bleu_status = false; | ||
| + | bool BP_Vert_status = false; | ||
| + | bool BP_Jaune_status = false; | ||
| + | bool BP_Rose_status = false; | ||
| + | |||
| + | // initialize the stepper library | ||
| + | Stepper myStepper(stepsTotalRevolution, IN1, IN3, IN2, IN4); | ||
| + | |||
| + | #include "WiFi.h" | ||
| + | #include "HTTPClient.h" | ||
| + | #include "ArduinoJson.h" | ||
| + | |||
| + | const char* ssid = ""; | ||
| + | const char* password = ""; | ||
| + | |||
| + | // Replace with the latitude and longitude to where you want to get the weather | ||
| + | String latitude = "48.390394"; | ||
| + | String longitude = "-4.486076"; | ||
| + | // Enter your location | ||
| + | String location = "Brest"; | ||
| + | // Type the timezone you want to get the time for | ||
| + | String timezone = "Europe/Lisbon"; | ||
| + | // Store date and time | ||
| + | String current_date; | ||
| + | String last_weather_update; | ||
| + | String temperature; | ||
| + | String humidity; | ||
| + | int is_day; | ||
| + | int weather_code = 0; | ||
| + | String weather_description; | ||
| + | |||
| + | // SET VARIABLE TO 0 FOR TEMPERATURE IN FAHRENHEIT DEGREES | ||
| + | #define TEMP_CELSIUS 1 | ||
| + | #if TEMP_CELSIUS | ||
| + | String temperature_unit = ""; | ||
| + | const char degree_symbol[] = "\u00B0C"; | ||
| + | #else | ||
| + | String temperature_unit = "&temperature_unit=fahrenheit"; | ||
| + | const char degree_symbol[] = "\u00B0F"; | ||
| + | #endif | ||
| + | |||
| + | |||
| + | /* | ||
| + | WMO Weather interpretation codes (WW)- Code Description | ||
| + | 0 Clear sky | ||
| + | 1, 2, 3 Mainly clear, partly cloudy, and overcast | ||
| + | 45, 48 Fog and depositing rime fog | ||
| + | 51, 53, 55 Drizzle: Light, moderate, and dense intensity | ||
| + | 56, 57 Freezing Drizzle: Light and dense intensity | ||
| + | 61, 63, 65 Rain: Slight, moderate and heavy intensity | ||
| + | 66, 67 Freezing Rain: Light and heavy intensity | ||
| + | 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity | ||
| + | 77 Snow grains | ||
| + | 80, 81, 82 Rain showers: Slight, moderate, and violent | ||
| + | 85, 86 Snow showers slight and heavy | ||
| + | 95 * Thunderstorm: Slight or moderate | ||
| + | 96, 99 * Thunderstorm with slight and heavy hail | ||
| + | */ | ||
| + | /* | ||
| + | Targets : | ||
| + | Clear sky : 0 -> 0 | ||
| + | Sun and clouds : 1 -> 1,2 | ||
| + | Clouds : 2 -> 3 | ||
| + | Sun and rain : 3 -> 51,53,56 | ||
| + | Rain : 4 -> 55,57,61,63,65,66,67,80,81,82 | ||
| + | Thunderstorm : 5 -> 95,96,99 | ||
| + | Snow : 6 -> 71,73,75,77,85,86 | ||
| + | Fog : 7 -> 45,48 | ||
| + | */ | ||
| + | |||
| + | int target = 0; | ||
| + | int slot_pos = 0; | ||
| + | |||
| + | void get_weather_description(int code) { | ||
| + | switch (code) { | ||
| + | case 0: | ||
| + | target = 0; | ||
| + | weather_description = "CLEAR SKY"; | ||
| + | break; | ||
| + | case 1: | ||
| + | case 2: | ||
| + | target = 1; | ||
| + | weather_description = "SUN AND CLOUDS"; | ||
| + | break; | ||
| + | case 3: | ||
| + | target = 2; | ||
| + | weather_description = "CLOUDS"; | ||
| + | break; | ||
| + | case 51: | ||
| + | case 53: | ||
| + | case 56: | ||
| + | target = 3; | ||
| + | weather_description = "SUN AND RAIN"; | ||
| + | break; | ||
| + | case 55: | ||
| + | case 57: | ||
| + | case 61: | ||
| + | case 63: | ||
| + | case 65: | ||
| + | case 66: | ||
| + | case 67: | ||
| + | case 80: | ||
| + | case 81: | ||
| + | case 82: | ||
| + | target = 4; | ||
| + | weather_description = "RAIN"; | ||
| + | break; | ||
| + | case 95: | ||
| + | case 96: | ||
| + | case 99: | ||
| + | target = 5; | ||
| + | weather_description = "THUNDERSTORM"; | ||
| + | break; | ||
| + | case 71: | ||
| + | case 73: | ||
| + | case 75: | ||
| + | case 77: | ||
| + | case 85: | ||
| + | case 86: | ||
| + | target = 6; | ||
| + | weather_description = "SNOW"; | ||
| + | break; | ||
| + | case 45: | ||
| + | case 48: | ||
| + | target = 7; | ||
| + | weather_description = "FOG"; | ||
| + | break; | ||
| + | default: | ||
| + | weather_description = "UNKNOWN WEATHER CODE"; | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void get_weather_data() { | ||
| + | if (WiFi.status() == WL_CONNECTED) { | ||
| + | HTTPClient http; | ||
| + | // Construct the API endpoint | ||
| + | String url = String("http://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude + "¤t=temperature_2m,relative_humidity_2m,is_day,precipitation,rain,weather_code" + temperature_unit + "&timezone=" + timezone + "&forecast_days=1"); | ||
| + | http.begin(url); | ||
| + | Serial.println("URL:"); | ||
| + | Serial.println(url); | ||
| + | int httpCode = http.GET(); // Make the GET request | ||
| + | if (httpCode > 0) { | ||
| + | // Check for the response | ||
| + | if (httpCode == HTTP_CODE_OK) { | ||
| + | String payload = http.getString(); | ||
| + | Serial.println("Request information:"); | ||
| + | Serial.println(payload); | ||
| + | // Parse the JSON to extract the time | ||
| + | JsonDocument doc; | ||
| + | DeserializationError error = deserializeJson(doc, payload); | ||
| + | if (!error) { | ||
| + | const char* datetime = doc["current"]["time"]; | ||
| + | temperature = String(doc["current"]["temperature_2m"]); | ||
| + | humidity = String(doc["current"]["relative_humidity_2m"]); | ||
| + | is_day = String(doc["current"]["is_day"]).toInt(); | ||
| + | weather_code = String(doc["current"]["weather_code"]).toInt(); | ||
| + | /*Serial.println(temperature); | ||
| + | Serial.println(humidity); | ||
| + | Serial.println(is_day); | ||
| + | Serial.println(weather_code); | ||
| + | Serial.println(String(timezone));*/ | ||
| + | // Split the datetime into date and time | ||
| + | String datetime_str = String(datetime); | ||
| + | int splitIndex = datetime_str.indexOf('T'); | ||
| + | current_date = datetime_str.substring(0, splitIndex); | ||
| + | last_weather_update = datetime_str.substring(splitIndex + 1, splitIndex + 9); // Extract time portion | ||
| + | } else { | ||
| + | Serial.print("deserializeJson() failed: "); | ||
| + | Serial.println(error.c_str()); | ||
| + | } | ||
| + | } | ||
| + | } else { | ||
| + | Serial.printf("GET request failed, error: %s\n", | ||
| + | http.errorToString(httpCode).c_str()); | ||
| + | } | ||
| + | http.end(); // Close connection | ||
| + | } else { | ||
| + | Serial.println("Not connected to Wi-Fi"); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | void rotate_disk(int target) { | ||
| + | Serial.println("TOURNE"); | ||
| + | int steps = target - slot_pos; | ||
| + | myStepper.step(stepsEighthRevolution); | ||
| + | Serial.println(slot_pos); | ||
| + | Serial.println(steps); | ||
| + | slot_pos = target; | ||
| + | } | ||
| + | |||
| + | bool is_button_pressed() { | ||
| + | if (BP_Bleu_status || BP_Vert_status || BP_Jaune_status || BP_Rose_status) { | ||
| + | return true; | ||
| + | } | ||
| + | return false; | ||
| + | } | ||
void setup() { | void setup() { | ||
| − | + | // set the speed at 5 rpm | |
| + | myStepper.setSpeed(15); | ||
| + | |||
| + | pinMode(BP_Bleu,INPUT_PULLDOWN); | ||
| + | pinMode(BP_Vert,INPUT_PULLDOWN); | ||
| + | //pinMode(BP_Jaune,INPUT_PULLDOWN); | ||
| + | //pinMode(BP_Rose,INPUT_PULLDOWN); | ||
| + | Serial.begin(115200); | ||
| + | // Connect to Wi-Fi | ||
| + | WiFi.begin(ssid, password); | ||
| + | Serial.print("Connecting"); | ||
| + | while (WiFi.status() != WL_CONNECTED) { | ||
| + | delay(500); | ||
| + | Serial.print("."); | ||
| + | } | ||
| + | Serial.print("\nConnected to Wi-Fi network with IP Address: "); | ||
| + | Serial.println(WiFi.localIP()); | ||
| + | // Create a display object | ||
} | } | ||
void loop() { | void loop() { | ||
| − | // | + | BP_Bleu_status = digitalRead(BP_Bleu); |
| + | BP_Vert_status = digitalRead(BP_Vert); | ||
| + | /*BP_Jaune_status = digitalRead(BP_Jaune); | ||
| + | BP_Rose_status = digitalRead(BP_Rose);*/ | ||
| + | if (is_button_pressed()) { | ||
| + | if (BP_Bleu_status) { // Brest | ||
| + | Serial.println("Bleu"); | ||
| + | latitude = "48.390394"; | ||
| + | longitude = "-4.486076"; | ||
| + | location = "Brest"; | ||
| + | timezone = "Europe/Lisbon"; | ||
| + | } | ||
| + | if (BP_Vert_status) { // San Antonio (Texas) | ||
| + | Serial.println("Vert"); | ||
| + | latitude = "29.4241"; | ||
| + | longitude = "-98.4936"; | ||
| + | location = "San Antonio"; | ||
| + | timezone = "America/Chicago"; | ||
| + | } | ||
| + | /*if (BP_Jaune_status) { // Le Cap (Afrique du Sud) | ||
| + | Serial.println("Jaune"); | ||
| + | latitude = "-33.9258"; | ||
| + | longitude = "18.4232"; | ||
| + | location = "Le Cap"; | ||
| + | timezone = "Africa/Cairo"; | ||
| + | } | ||
| + | if (BP_Rose_status) { // Oulan-Bator (Mongolie) | ||
| + | Serial.println("Rose"); | ||
| + | latitude = "47.9077"; | ||
| + | longitude = "106.8832"; | ||
| + | location = "Ulaanbaatar"; | ||
| + | timezone = "Asia/Bangkok"; | ||
| + | }*/ | ||
| + | get_weather_data(); | ||
| + | get_weather_description(weather_code); | ||
| + | int tempInt = temperature.toInt(); | ||
| + | Serial.print("Weather description: "); | ||
| + | Serial.println(weather_description); | ||
| + | Serial.print("Target: "); | ||
| + | Serial.println(target); | ||
| + | if (target != slot_pos) { | ||
| + | rotate_disk(target); | ||
| + | } | ||
| + | delay(500); | ||
| + | } | ||
} | } | ||
| Ligne 33 : | Ligne 338 : | ||
indiquer autant d'étape que nécessaire, chacune illustrée par des images (photo, dessins, ...) | indiquer autant d'étape que nécessaire, chacune illustrée par des images (photo, dessins, ...) | ||
| − | ===étape 1=== | + | ===étape 1 - Conception du concept=== |
| − | ===étape 2=== | + | |
| − | ===étape ...=== | + | Définition du fonctionnement global : |
| + | |||
| + | - Répartition des rôles | ||
| + | |||
| + | - Association des types de météos avec des symboles | ||
| + | |||
| + | - Discussion pour le prototype | ||
| + | |||
| + | ===étape 2 - Prototype physique=== | ||
| + | |||
| + | Réalisation d’un prototype en carton : | ||
| + | |||
| + | - Boîte carrée | ||
| + | |||
| + | - Face supérieure avec symboles météo | ||
| + | |||
| + | - Disque circulaire avec une ouverture laissant apparaître un seul symbole | ||
| + | |||
| + | [[Fichier:Youenn the ripper.jpg|300px]] | ||
| + | [[Fichier:Manar qui dessine.jpg|300px]] | ||
| + | [[Fichier:Lescerclesaudebut.jpg|200px]] | ||
| + | [[Fichier:Laboiboiteaveclesboutons.jpg|200px]] | ||
| + | |||
| + | ===étape 3 - Programmation et Electronique=== | ||
| + | |||
| + | V0 du code pour faire des tests : | ||
| + | |||
| + | [[Fichier:Rouedelameteophotocode.jpg|300px]] | ||
| + | |||
| + | Test du moteur avec et sans la maquette : | ||
| + | |||
| + | [[Fichier:Plaquemoteur.jpg|200px]] | ||
| + | [[Fichier:Plaqueplusroue.jpg|200px]] | ||
| + | |||
| + | [[Fichier:Lavideoquifaitdesimagesquibougentla.mp4|400px]] | ||
| + | |||
| + | Mr Code : | ||
| + | |||
| + | [[Fichier:Chevalier de la meteo.jpg|200px]] | ||
| + | |||
| + | ===étape 4 - Concrétisation=== | ||
| + | |||
| + | Réalisation du projet final en bois : | ||
| + | |||
| + | - Boite octogonale | ||
| + | |||
| + | - Pièces faites à la découpeuse laser | ||
| + | |||
| + | - Fixation du moteur au couvercle avec des vis à bois | ||
| + | |||
| + | [[Fichier:Decoupeuseroue1.jpg|200px]] | ||
| + | [[Fichier:Decoupeuseroue2.jpg|200px]] | ||
| + | [[Fichier:Montageboomboompafpaf.jpg|400px]] | ||
| + | |||
===Troubleshouting=== | ===Troubleshouting=== | ||
| − | + | ||
| + | On a eu des problèmes avec la maquette, le moteur ne faisait pas bien tourner le cercle --> on a retravaillé la fixation entre les éléments et on a fait attention à bien calibrer le moteur sur les symboles météo. | ||
| + | |||
| + | Problème de taille du fichier pour la découpeuse laser --> diminution de la taille de la boite + 2 impression laser pour tout faire | ||
| + | |||
| + | Fixation du moteur --> dépassement des vis à bois --> au final ça passe bien il faudra juste faire attention en mettant la plaque tournante après | ||
| + | |||
| + | Problème pour lier l'API avec le moteur dans le code --> déboggage | ||
| + | |||
| + | Abandon de l'ajout de LED par manque de temps | ||
| + | |||
| + | ===La carte de puissance du moteur ne marche plus donc il faudrait changer ça et retester pour travailler sur le projet=== | ||
==Sources et documentation complémentaire== | ==Sources et documentation complémentaire== | ||
| Ligne 54 : | Ligne 423 : | ||
==Elément de présentation== | ==Elément de présentation== | ||
| − | + | ||
| + | [[Fichier:Ppt la roue de la meteo presentation.png|500px]] | ||
| + | |||
| + | |||
| + | Lien diapo: https://www.canva.com/design/DAG_Jjv8fjs/1t5NMPpTc0cexBQhQH--Og/edit?utm_content=DAG_Jjv8fjs&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton | ||
==ne pas modifier sous cette ligne== | ==ne pas modifier sous cette ligne== | ||
[[Catégorie:Enib2026]] | [[Catégorie:Enib2026]] | ||
Version actuelle datée du 22 janvier 2026 à 16:50
Titre de la fiche expérience : La roue de la météo – Objet connecté ludique
Sommaire
description (résumé)
La Roue de la météo est un objet connecté ludique permettant d’afficher la météo d’une ville de manière visuelle et interactive. L’utilisateur sélectionne une ville via un bouton physique, la carte ESP32 récupère les données météorologiques en ligne et allume une LED correspondant au type de météo. Ensuite un moteur pas à pas va faire tourner un disque sur le dessus de la boîte jusqu'au symbole météo correspondant.
Introduction
Dans le cadre du hackathon ENIB 2026 avec les petits débrouillards, l’objectif est de concevoir un objet connecté à la fois simple, utile et ludique. Notre projet s’inscrit dans cette démarche en proposant une approche ludique pour savoir la météo d'une ville.
La Roue de la météo privilégie une interaction physique et intuitive, sans écran, afin de rendre l’information météo accessible et amusante. Le projet combine électronique, programmation, design et fabrication d’un objet.
outil et matériel
Électronique :
Carte ESP32-S ; LEDs (1 par symbole météo) ; Résistances pour LEDs ; Boutons poussoirs colorés (1 par ville) ; Câbles de connexion ; Alimentation (USB)
Fabrication :
Carton (prototype) ; Bois (version finale prévue) ; Cutter / ciseaux ; Colle / adhésif ; Support circulaire rotatif avec des symbole météo (découpeuse laser)
Logiciel :
Arduino ; Bibliothèques ESP32 ; API météo (OpenMétéo)
fichiers à joindre
code, ficher d'impression 3D, de découpe laser ou vinyle, ...
Mettre du code Arduino
Modifier les lignes 31 et 32 pour connecter un wifi ou un partage de connexion et faire fonctionner le lien entre le projet et l'API
1
2 #include <Stepper.h>
3
4 const int stepsTotalRevolution = 2048; // tour complet
5 const int stepsEighthRevolution = 256; // 1/8 de tour
6
7 // ULN2003 Motor Driver Pins
8 #define IN1 26
9 #define IN2 27
10 #define IN3 14
11 #define IN4 12
12
13 // Buttons Pins
14 #define BP_Bleu 16
15 #define BP_Vert 17
16 #define BP_Jaune 19
17 #define BP_Rose 34
18
19 bool BP_Bleu_status = false;
20 bool BP_Vert_status = false;
21 bool BP_Jaune_status = false;
22 bool BP_Rose_status = false;
23
24 // initialize the stepper library
25 Stepper myStepper(stepsTotalRevolution, IN1, IN3, IN2, IN4);
26
27 #include "WiFi.h"
28 #include "HTTPClient.h"
29 #include "ArduinoJson.h"
30
31 const char* ssid = "";
32 const char* password = "";
33
34 // Replace with the latitude and longitude to where you want to get the weather
35 String latitude = "48.390394";
36 String longitude = "-4.486076";
37 // Enter your location
38 String location = "Brest";
39 // Type the timezone you want to get the time for
40 String timezone = "Europe/Lisbon";
41 // Store date and time
42 String current_date;
43 String last_weather_update;
44 String temperature;
45 String humidity;
46 int is_day;
47 int weather_code = 0;
48 String weather_description;
49
50 // SET VARIABLE TO 0 FOR TEMPERATURE IN FAHRENHEIT DEGREES
51 #define TEMP_CELSIUS 1
52 #if TEMP_CELSIUS
53 String temperature_unit = "";
54 const char degree_symbol[] = "\u00B0C";
55 #else
56 String temperature_unit = "&temperature_unit=fahrenheit";
57 const char degree_symbol[] = "\u00B0F";
58 #endif
59
60
61 /*
62 WMO Weather interpretation codes (WW)- Code Description
63 0 Clear sky
64 1, 2, 3 Mainly clear, partly cloudy, and overcast
65 45, 48 Fog and depositing rime fog
66 51, 53, 55 Drizzle: Light, moderate, and dense intensity
67 56, 57 Freezing Drizzle: Light and dense intensity
68 61, 63, 65 Rain: Slight, moderate and heavy intensity
69 66, 67 Freezing Rain: Light and heavy intensity
70 71, 73, 75 Snow fall: Slight, moderate, and heavy intensity
71 77 Snow grains
72 80, 81, 82 Rain showers: Slight, moderate, and violent
73 85, 86 Snow showers slight and heavy
74 95 * Thunderstorm: Slight or moderate
75 96, 99 * Thunderstorm with slight and heavy hail
76 */
77 /*
78 Targets :
79 Clear sky : 0 -> 0
80 Sun and clouds : 1 -> 1,2
81 Clouds : 2 -> 3
82 Sun and rain : 3 -> 51,53,56
83 Rain : 4 -> 55,57,61,63,65,66,67,80,81,82
84 Thunderstorm : 5 -> 95,96,99
85 Snow : 6 -> 71,73,75,77,85,86
86 Fog : 7 -> 45,48
87 */
88
89 int target = 0;
90 int slot_pos = 0;
91
92 void get_weather_description(int code) {
93 switch (code) {
94 case 0:
95 target = 0;
96 weather_description = "CLEAR SKY";
97 break;
98 case 1:
99 case 2:
100 target = 1;
101 weather_description = "SUN AND CLOUDS";
102 break;
103 case 3:
104 target = 2;
105 weather_description = "CLOUDS";
106 break;
107 case 51:
108 case 53:
109 case 56:
110 target = 3;
111 weather_description = "SUN AND RAIN";
112 break;
113 case 55:
114 case 57:
115 case 61:
116 case 63:
117 case 65:
118 case 66:
119 case 67:
120 case 80:
121 case 81:
122 case 82:
123 target = 4;
124 weather_description = "RAIN";
125 break;
126 case 95:
127 case 96:
128 case 99:
129 target = 5;
130 weather_description = "THUNDERSTORM";
131 break;
132 case 71:
133 case 73:
134 case 75:
135 case 77:
136 case 85:
137 case 86:
138 target = 6;
139 weather_description = "SNOW";
140 break;
141 case 45:
142 case 48:
143 target = 7;
144 weather_description = "FOG";
145 break;
146 default:
147 weather_description = "UNKNOWN WEATHER CODE";
148 break;
149 }
150 }
151
152 void get_weather_data() {
153 if (WiFi.status() == WL_CONNECTED) {
154 HTTPClient http;
155 // Construct the API endpoint
156 String url = String("http://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude + "¤t=temperature_2m,relative_humidity_2m,is_day,precipitation,rain,weather_code" + temperature_unit + "&timezone=" + timezone + "&forecast_days=1");
157 http.begin(url);
158 Serial.println("URL:");
159 Serial.println(url);
160 int httpCode = http.GET(); // Make the GET request
161 if (httpCode > 0) {
162 // Check for the response
163 if (httpCode == HTTP_CODE_OK) {
164 String payload = http.getString();
165 Serial.println("Request information:");
166 Serial.println(payload);
167 // Parse the JSON to extract the time
168 JsonDocument doc;
169 DeserializationError error = deserializeJson(doc, payload);
170 if (!error) {
171 const char* datetime = doc["current"]["time"];
172 temperature = String(doc["current"]["temperature_2m"]);
173 humidity = String(doc["current"]["relative_humidity_2m"]);
174 is_day = String(doc["current"]["is_day"]).toInt();
175 weather_code = String(doc["current"]["weather_code"]).toInt();
176 /*Serial.println(temperature);
177 Serial.println(humidity);
178 Serial.println(is_day);
179 Serial.println(weather_code);
180 Serial.println(String(timezone));*/
181 // Split the datetime into date and time
182 String datetime_str = String(datetime);
183 int splitIndex = datetime_str.indexOf('T');
184 current_date = datetime_str.substring(0, splitIndex);
185 last_weather_update = datetime_str.substring(splitIndex + 1, splitIndex + 9); // Extract time portion
186 } else {
187 Serial.print("deserializeJson() failed: ");
188 Serial.println(error.c_str());
189 }
190 }
191 } else {
192 Serial.printf("GET request failed, error: %s\n",
193 http.errorToString(httpCode).c_str());
194 }
195 http.end(); // Close connection
196 } else {
197 Serial.println("Not connected to Wi-Fi");
198 }
199 }
200
201 void rotate_disk(int target) {
202 Serial.println("TOURNE");
203 int steps = target - slot_pos;
204 myStepper.step(stepsEighthRevolution);
205 Serial.println(slot_pos);
206 Serial.println(steps);
207 slot_pos = target;
208 }
209
210 bool is_button_pressed() {
211 if (BP_Bleu_status || BP_Vert_status || BP_Jaune_status || BP_Rose_status) {
212 return true;
213 }
214 return false;
215 }
216
217 void setup() {
218 // set the speed at 5 rpm
219 myStepper.setSpeed(15);
220
221 pinMode(BP_Bleu,INPUT_PULLDOWN);
222 pinMode(BP_Vert,INPUT_PULLDOWN);
223 //pinMode(BP_Jaune,INPUT_PULLDOWN);
224 //pinMode(BP_Rose,INPUT_PULLDOWN);
225
226 Serial.begin(115200);
227 // Connect to Wi-Fi
228 WiFi.begin(ssid, password);
229 Serial.print("Connecting");
230 while (WiFi.status() != WL_CONNECTED) {
231 delay(500);
232 Serial.print(".");
233 }
234 Serial.print("\nConnected to Wi-Fi network with IP Address: ");
235 Serial.println(WiFi.localIP());
236 // Create a display object
237 }
238
239 void loop() {
240 BP_Bleu_status = digitalRead(BP_Bleu);
241 BP_Vert_status = digitalRead(BP_Vert);
242 /*BP_Jaune_status = digitalRead(BP_Jaune);
243 BP_Rose_status = digitalRead(BP_Rose);*/
244
245 if (is_button_pressed()) {
246 if (BP_Bleu_status) { // Brest
247 Serial.println("Bleu");
248 latitude = "48.390394";
249 longitude = "-4.486076";
250 location = "Brest";
251 timezone = "Europe/Lisbon";
252 }
253 if (BP_Vert_status) { // San Antonio (Texas)
254 Serial.println("Vert");
255 latitude = "29.4241";
256 longitude = "-98.4936";
257 location = "San Antonio";
258 timezone = "America/Chicago";
259 }
260 /*if (BP_Jaune_status) { // Le Cap (Afrique du Sud)
261 Serial.println("Jaune");
262 latitude = "-33.9258";
263 longitude = "18.4232";
264 location = "Le Cap";
265 timezone = "Africa/Cairo";
266 }
267 if (BP_Rose_status) { // Oulan-Bator (Mongolie)
268 Serial.println("Rose");
269 latitude = "47.9077";
270 longitude = "106.8832";
271 location = "Ulaanbaatar";
272 timezone = "Asia/Bangkok";
273 }*/
274 get_weather_data();
275 get_weather_description(weather_code);
276 int tempInt = temperature.toInt();
277 Serial.print("Weather description: ");
278 Serial.println(weather_description);
279 Serial.print("Target: ");
280 Serial.println(target);
281 if (target != slot_pos) {
282 rotate_disk(target);
283 }
284 delay(500);
285 }
286 }
étapes de fabrication
indiquer autant d'étape que nécessaire, chacune illustrée par des images (photo, dessins, ...)
étape 1 - Conception du concept
Définition du fonctionnement global :
- Répartition des rôles
- Association des types de météos avec des symboles
- Discussion pour le prototype
étape 2 - Prototype physique
Réalisation d’un prototype en carton :
- Boîte carrée
- Face supérieure avec symboles météo
- Disque circulaire avec une ouverture laissant apparaître un seul symbole
étape 3 - Programmation et Electronique
V0 du code pour faire des tests :
Test du moteur avec et sans la maquette :
Mr Code :
étape 4 - Concrétisation
Réalisation du projet final en bois :
- Boite octogonale
- Pièces faites à la découpeuse laser
- Fixation du moteur au couvercle avec des vis à bois
Troubleshouting
On a eu des problèmes avec la maquette, le moteur ne faisait pas bien tourner le cercle --> on a retravaillé la fixation entre les éléments et on a fait attention à bien calibrer le moteur sur les symboles météo.
Problème de taille du fichier pour la découpeuse laser --> diminution de la taille de la boite + 2 impression laser pour tout faire
Fixation du moteur --> dépassement des vis à bois --> au final ça passe bien il faudra juste faire attention en mettant la plaque tournante après
Problème pour lier l'API avec le moteur dans le code --> déboggage
Abandon de l'ajout de LED par manque de temps
La carte de puissance du moteur ne marche plus donc il faudrait changer ça et retester pour travailler sur le projet
Sources et documentation complémentaire
- Rédаctiоn et illustratiоn :
Pоur tоus vоs trаvauх, qu'ils sоient écrits оu visuels, l'utilisatiоn de l'intеlligеnce artificiеllе générativе, que сe sоit pоur le teхte оu les images, n'еst pas conseillé.
- Prоgrammаtiоn :
En сe qui cоncernе la prоgrаmmatiоn, il est еssentiеl de ne pаs faire dе l'IA vоtrе prеmier rеcоurs. Cоncеntrеz-vоus d'abоrd sur vоtre prоpre lоgiquе, votre experience еt lеs ressоurcеs disponibles.
- Transpаrence et dосumеntatiоn :
Si vоus utilisеz l'IA pоur déblоquer оu améliоrеr une pаrtiе de vоtre cоdе, il est cruciаl de l'indiquеr сlairеmеnt dans vоtre dосumentatiоn tеchniquе.
- Traçabilité :
Chаque ехtrait de cоde généré avес l'аidе de l'IA dоit êtrе accоmpagné de la sоurce, ainsi que du prоmpt eхact qui a été utilisé pоur sа créatiоn, afin d'аssurеr une évaluatiоn clаire dе vоtre prоcessus.