POCL : The Big Red One

De Les Fabriques du Ponant
Révision datée du 9 décembre 2022 à 16:25 par Maximechort (discussion | contributions) (résumé du projet)
Aller à : navigation, rechercher

résumé du projet

 Nous sommes parties de l'idée d'une donnée peu utile qui peut paraitre absurde, mais dont le concept est rigolo, il s'agit d'un gros bouton rouge sur un dispositif qui compte le nombre de fous ou les personnes, on appuie dessus. Ce dispositif pourrait donc être installé dans plein d'endroits différant et permet de voir ou les gens son le plus curieux et le plus nombreux. À partir de cette idée amusante, nous nous sommes intéressé au potentiel que pourrais avoir ce dispositif pour passer un message plus complexe, car en attirant les personnes avec un gros bouton rouge au milieu d'une boite de manière ludique, on peut les emmener vers des questionnements plus profonds.

membres du projet

utilisateur:AlexisB

utilisateur:MsNicolasp

utilisateur:Adame Rhrich

utilisateur:Ayman

utilisateur:Loodwig Houssais

utilisateur:Sévrim

utilisateur:Khachatur

Matériel utilisé

-Matériel-

-Carte arduino mini

-Écran max7219

-Bois

-Colle a bois

-Découpeuse laser

-Imprimante 3D


-Logiciel-

-Inkscape (image vectoriel)

- Wix (création de site)

-Blender (Modélisation 3D)

-Cura (préparation d'impression 3D)

prototype du projet

Nous avons commencé par construire la boite de ce projet avec MakerCase https://fr.makercase.com/#/ Nous avons ensuite rajouté les décors de la boite sur Inkscape. Puis nous l'avons découpé sur la découpeuse Laser.

300px

Diaporama

  1 /* =========================================================================================================
  2  * 
  3  *                              POCL Trends - Hackathon
  4  *          
  5  * ---------------------------------------------------------------------------------------------------------
  6  * Les petits Débrouillards - décembre 2022 - CC-By-Sa http://creativecommons.org/licenses/by-nc-sa/3.0/
  7  * ========================================================================================================= */
  8 
  9 // Bibliothèques requises
 10 // ATTENTION AUX MAJUSCULES & MINUSCULES ! Sinon d'autres bibliothèques, plus ou moins valides, seraient utilisées.
 11 
 12 #include <WiFiManager.h>                          // Gestion de la connexion Wi-Fi (recherche de points d'accès)  
 13 #include <ESP8266HTTPClient.h>                    // Gestion des requêtes HTTP vers le serveur
 14 #include <WiFiClient.h>                           // Gestion de la connexion (HTTP) à un serveur de données
 15 #include <ArduinoJson.h>                          // Fonctions de décodage JSON des réponses du serveur. 
 16 #include "LiquidCrystal_I2C.h"                    // Gestion de l'afficheur LCD 1602 avec module i2c
 17 #include <MD_Parola.h>
 18 #include <MD_MAX72xx.h>                           // Gestion afficheur MAX7219
 19 #include <SPI.h>
 20 
 21 
 22 // Variables globales
 23 
 24 WiFiManager myWiFiManager;                        // Création de mon instance de WiFiManager.
 25 const char* mySSID   = "AP_PetitDeb" ;            // Nom de la carte en mode Point d'Accès.
 26 const char* mySecKey = "PSWD1234" ;               // Mot de passe associé, 8 caractères au minimum.
 27 
 28 /*
 29 char* Data_HOST = "data.rennesmetropole.fr";      // Serveur web hébergeant les données qui nous intéressent
 30 int   Data_PORT = 443;                            // Port sur lequel envoyer la requête
 31 char* Data_REQUEST =                              // Requête (sur cet exemple : demande de l'état du trafic au point
 32                                                   // 31553, correspondant à la porte de Saint-Malo de la rocade de Rennes 
 33       "/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553";  
 34 */
 35 
 36 char* GL_HTTP_REQUEST = "http://nodered.lenuage.io/trends" ;  // Requête (appel à un serveur intermédiaire compliant les infos de googles trends)
 37 
 38 const int MAX_RESPONSE_SIZE = 8000 ;                          // Taille max de la réponse attendue d'un serveur. A modifier en fonction du besoin.
 39 char GL_httpResponse[MAX_RESPONSE_SIZE] ;                     // Buffer qui contiendra la réponse du serveur.
 40 
 41 // LiquidCrystal_I2C LCD(0x27,16,2);                             // Définition de notre écran LCD.
 42 
 43 //------------------------------
 44 // Define hardware type, size, and output pins:
 45 #define HARDWARE_TYPE MD_MAX72XX::FC16_HW
 46 #define MAX_DEVICES 4
 47 #define CS_PIN D3
 48 #define DATA_PIN D2
 49 #define CLK_PIN D1
 50 
 51 // Create a new instance of the MD_Parola class with hardware SPI connection:
 52 // Setup for software SPI:
 53 MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);
 54 
 55 //-------------------------------
 56 
 57   
 58 #define TEN_SECONDS 10000                                     // On appelera le serveur de données toutes les 10000 ms = 10 secondes.
 59 unsigned long myWakeUp ;                                      // Timer mis en place pour limiter le nombre d'appels au serveur de données.
 60 
 61 /* --------------------------------------------------------------------------------------------------------------
 62  *  GetServerResponse() : Envoi requête HTTP au serveur et récupération de la réponse
 63  *  valeur de retour : 1 = ok, 0 = pas de connexion serveur, -1 = erreur HTTP GET, -2 = réponse anormale (trop de caractères)
 64  *  ------------------------------------------------------------------------------------------------------------- */
 65 int getServerResponse() {
 66 
 67     WiFiClient myWiFiClient;
 68     HTTPClient myHTTPClient;
 69 
 70     /*
 71      * Connexion au serveur ... 
 72      */
 73 
 74     if (!myHTTPClient.begin(myWiFiClient, GL_HTTP_REQUEST)) {  
 75         Serial.printf("[HTTP} Unable to connect\n");
 76         return(0) ;
 77     }
 78 
 79   /*
 80    * Envoi de l'entête HTTP ...
 81    */
 82    
 83     Serial.print("[HTTP] GET...\n");
 84     int httpCode = myHTTPClient.GET();
 85 
 86     if (httpCode <= 0) {
 87         Serial.printf("[HTTP] GET... failed, error: %s\n", myHTTPClient.errorToString(httpCode).c_str());   
 88         myHTTPClient.end();
 89         return(-1) ;
 90     }
 91 
 92     /*
 93      * Requête HTTP OK ! --> On fait à nouveau clignoter les leds en bleu (3 flashs longs).
 94      */
 95 
 96     /*
 97      * Lecture des données qui nous intéressent. On récupère caractère par caractère, (semble plus sûr que la fonction payload
 98      * qui plante de temps en temps, d'après les forums. De plus, cela permet de faire défiler les leds pour montrer que 
 99      * ça suit son cours ...
100      */
101 
102     int myIndex = 0 ; 
103     while(myWiFiClient.available() || myWiFiClient.connected()){
104       
105         int myResp = myWiFiClient.read();
106         if (myResp == -1) {
107             break ;
108         }
109            
110         GL_httpResponse[myIndex] = myResp;
111         // Serial.print(GL_httpResponse[myIndex]) ;
112         if (myIndex++ > MAX_RESPONSE_SIZE) {
113             Serial.println("Réponse trop longue : " + String(MAX_RESPONSE_SIZE) + "caractères, et ne peut pas être traitée") ;
114             myWiFiClient.stop();
115             return(-2);
116         }
117 
118     }
119         
120     
121     /*
122      * Tout s'est bien passé --> on passe les leds en vert, et on se déconnecte du serveur.
123      */
124        
125     myHTTPClient.end();
126     return(1) ;
127 
128 }
129 
130 
131 
132 /* --------------------------------------------------------------------------------------------------------
133  *  showJSONAnswer : Décodage de la structure de données JSON
134  *  Paramètres :
135  *    - pResponse : endroit se trouve la réponse (au format JSON) du serveur
136  *    - pRespMax  : nombre max de caractères autorisés pour la réponse
137  * -------------------------------------------------------------------------------------------------------- */
138 void showJSONAnswer(char *pResponse, int pRespMax) {
139 
140     // Création de notre structure JSON
141     // Le besoin en mémoire (capacity) doit être vérifié sur l'assistant https://arduinojson.org/v6/assistant/
142     // 1) dans la première page de l'assistant, sélectionnez le processeur (par exemple "ESP8266"), le mode
143     //    "Deserialize", et le type d'entrée "char*", puis cliquez sur le bouton "Netx:JSON"
144     // 2) Lancez votre requête depuis un navigateur. Dans notre exemple, tapez dans la barre d'adresse :
145     //      "https://data.rennesmetropole.fr/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553"
146     // 3) Recopiez la réponse obtenue - sous sa forme "Données Brutes" du navigateur vers l'assistant
147     // 4) L'assistant va alors préconiser le bon objet à créer (StaticJsonDocument ou DynamicJsonDocument),
148     //    ainsi que la taille à réserver. L'assistant va même proposer un exemple de programme exploitant toutes 
149     //    les informations de la structure JSON. 
150     // Pour notre exemple, l'assistant a proposé la définition qui suit.
151     
152     StaticJsonDocument<300> doc;
153 
154     // Décodage de la réponse JSON.
155     // La fonction deserializeJson va transformer la réponse "texte" du serveur, en une structure de données recopiée
156     // dans la variable 'doc', où il sera ensuite facile d'aller chercher les informations souhaitées.
157      
158     DeserializationError error = deserializeJson(doc, pResponse, pRespMax);
159     if (error) {
160         Serial.println("--- Décodage réponse JSON KO, code " + String(error.f_str())) ;
161         return;
162     }
163     Serial.println("--- Décodage réponse JSON OK !") ;
164 
165     // Nous pouvons maintenant extraire facilement les informations qui nous intéressent,
166     // en n'oubliant pas le niveau de profondeur de la donnée au sein de la structure JSON. 
167     // Ce niveau de profondeur est incrémenté par le nombre de '{' ou '[' rencontrés, et 
168     // décrémenté lors de la rencontre des ']' et {}'. Sur notre exemple 'rocade de Rennes',
169     // cela donne ceci :
170     //       +-----------------------------------------------------------------+
171     //       |   {                                                             | ... Entrée niveau 1
172     //       |      "nhits": 1,                                                |
173     //       |      "parameters": {                                            | ... Entrée niveau 2
174     //       |          "dataset": "etat-du-trafic-en-temps-reel",             |
175     //       |          (...)                                                  |
176     //       |      },                                                         | ... Retour niveau 1
177     //       |      "records": [                                               | ... Début d'un tableau : niveau 2
178     //       |          {                                                      | ... Entrée niveau 3
179     //       |              (...)                                              |                                                           |
180     //       |              "fields": {                                        | ... Entrée niveau 4
181     //       |                   (...)                                         |
182     //       |                   "averagevehiclespeed": 88,                    |
183     //       |                   (...)                                         |
184     //       |                   "datetime": "2022-11-30T11:57:00+01:00",      |
185     //       +-----------------------------------------------------------------+
186     // ... et donc :
187     //  - (1er niveau) --------- doc["nhits"] donnera la valeur 1,
188     //  - (2ème niveau) -------- doc["parameters"]["dataset"] donnera la valeur "etat-du-trafic-en-temps-reel"
189     //  - (4ème niveau) -------- doc["records"][0]["fields"]["averagevehiclespeed"] donnera la valeur 87
190 
191     // Extraction et affichage sur le port série de trois  valeurs
192 
193     char myMessage[80];
194     for (int i = 0; i < 5; i++) { 
195         const char* Title = doc[i] ;
196         Serial.println("Trend n° " + String(i) + " : " + String(Title)) ;
197         // LCD.setCursor(1, 0);
198         sprintf(myMessage, "Trends n° %d - %s", i, Title) ;
199         // LCD.print(myMessage);
200 
201 
202         myDisplay.print(myMessage);
203 
204         
205         delay(500) ;
206         // LCD.clear() ;
207     }
208 
209 }
210 
211 /* --------------------------------------------------------------------------------------------------------
212  *  SETUP : Initialisation
213  * -------------------------------------------------------------------------------------------------------- */
214 void setup() {
215 
216     // Initialisation de la liaison série, affichage 1er message
217 
218     Serial.begin(115200);
219     delay(100) ;
220     Serial.println(); 
221     Serial.println("-----------------------") ;
222     Serial.println("Exemple extraction JSON") ;
223     Serial.println("-----------------------") ;
224 
225     // Initialisation de l'écran LCD.
226 
227    // LCD.init(); // initialisation de l'afficheur
228    // LCD.backlight();
229    
230     myDisplay.begin();
231     myDisplay.displayClear();
232 
233     // Tentative de connexion au Wi-Fi. Si la carte n'a pas réussi  se connecter au dernier Point d'Accès connu,
234     // alors elle va se positionner en mode Point d'Accès, demandera sur l'adresse 192.168.4.1 quel nouveau
235     // Point d'Accès choisir. Par défaut, on restera bloqué tant que l'utilisateur n'aura pas fait de choix.
236     
237     Serial.println("Connexion au Wi-Fi ...");
238     //  LCD.print("Connexion au Wi-Fi ...") ;
239    myDisplay.print("Connexion au Wi-Fi ...");
240     if (myWiFiManager.autoConnect(mySSID, mySecKey)) {
241         Serial.println(); Serial.print("Connecté ! Adresse IP : ");
242         // LCD.print("Connecté ! Adresse IP : ") ; LCD.print(WiFi.localIP()) ;
243         Serial.println(WiFi.localIP());
244     }
245     else {
246         Serial.println("Connexion Wi-Fi KO :-(");     
247     }
248 
249     // Initialisation du timer qui sera testé dans loop() - pour faire appel au serveur seulement toutes les 10 secondes 
250     // millis() est une fonction système donnant le nombre de ms depuis le lancement ou la réinitialisation de la carte.
251 
252     unsigned long myWakeUp = millis() + TEN_SECONDS ;
253 
254 }
255 
256 /* --------------------------------------------------------------------------------------------------------------
257  *  LOOP : fonction appelée régulièrement par le système
258  *  ------------------------------------------------------------------------------------------------------------- */
259 void loop() { 
260 
261     unsigned long myNow = millis() ;
262     if (myNow >= myWakeUp) {
263         Serial.println("Wake Up ! Nouvelle demande au serveur ...") ;
264         if (getServerResponse() == 1) {
265             Serial.println("Réponse reçue du serveur, lancement analyse JSON ...") ;
266             showJSONAnswer(GL_httpResponse, MAX_RESPONSE_SIZE) ;
267         }
268         myWakeUp = myNow + TEN_SECONDS ;         
269     }
270 
271 }