POCL : The Big Red One : Différence entre versions
(→membres du projet) |
(→Diaporama) |
||
(27 révisions intermédiaires par 4 utilisateurs non affichées) | |||
Ligne 1 : | Ligne 1 : | ||
==résumé du projet== | ==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== | ==membres du projet== | ||
[[utilisateur:AlexisB]] | [[utilisateur:AlexisB]] | ||
− | |||
− | ==prototype | + | [[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. | ||
+ | |||
+ | [[fichier:BuzzerBigRed1.png|300px]] | ||
+ | |||
+ | ===Bouton à imprimer en 3D=== | ||
+ | [[Fichier:POCL2022-BoutonChampi.stl]] | ||
+ | ===Découpe Laser de la boite=== | ||
+ | [[Media:POCL2022-BoxBigRedONE.svg|Téléchargez le SVG]] | ||
+ | |||
+ | ===QR-Code pour se rendre sur le site web=== | ||
+ | [[Fichier:POCL2022-QRcodeSite.png|300px]] | ||
+ | |||
==Diaporama== | ==Diaporama== | ||
+ | <syntaxhighlight lang="C++" line> | ||
+ | /* ========================================================================================================= | ||
+ | * | ||
+ | * POCL Trends - Hackathon | ||
+ | * | ||
+ | * --------------------------------------------------------------------------------------------------------- | ||
+ | * Les petits Débrouillards - décembre 2022 - CC-By-Sa http://creativecommons.org/licenses/by-nc-sa/3.0/ | ||
+ | * ========================================================================================================= */ | ||
+ | |||
+ | // Bibliothèques requises | ||
+ | // ATTENTION AUX MAJUSCULES & MINUSCULES ! Sinon d'autres bibliothèques, plus ou moins valides, seraient utilisées. | ||
+ | |||
+ | #include <WiFiManager.h> // Gestion de la connexion Wi-Fi (recherche de points d'accès) | ||
+ | #include <ESP8266HTTPClient.h> // Gestion des requêtes HTTP vers le serveur | ||
+ | #include <WiFiClient.h> // Gestion de la connexion (HTTP) à un serveur de données | ||
+ | #include <ArduinoJson.h> // Fonctions de décodage JSON des réponses du serveur. | ||
+ | #include "LiquidCrystal_I2C.h" // Gestion de l'afficheur LCD 1602 avec module i2c | ||
+ | #include <MD_Parola.h> | ||
+ | #include <MD_MAX72xx.h> // Gestion afficheur MAX7219 | ||
+ | #include <SPI.h> | ||
+ | |||
+ | |||
+ | // Variables globales | ||
+ | |||
+ | WiFiManager myWiFiManager; // Création de mon instance de WiFiManager. | ||
+ | const char* mySSID = "AP_PetitDeb" ; // Nom de la carte en mode Point d'Accès. | ||
+ | const char* mySecKey = "PSWD1234" ; // Mot de passe associé, 8 caractères au minimum. | ||
+ | |||
+ | /* | ||
+ | char* Data_HOST = "data.rennesmetropole.fr"; // Serveur web hébergeant les données qui nous intéressent | ||
+ | int Data_PORT = 443; // Port sur lequel envoyer la requête | ||
+ | char* Data_REQUEST = // Requête (sur cet exemple : demande de l'état du trafic au point | ||
+ | // 31553, correspondant à la porte de Saint-Malo de la rocade de Rennes | ||
+ | "/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553"; | ||
+ | */ | ||
+ | |||
+ | char* GL_HTTP_REQUEST = "http://nodered.lenuage.io/trends" ; // Requête (appel à un serveur intermédiaire compliant les infos de googles trends) | ||
+ | |||
+ | const int MAX_RESPONSE_SIZE = 8000 ; // Taille max de la réponse attendue d'un serveur. A modifier en fonction du besoin. | ||
+ | char GL_httpResponse[MAX_RESPONSE_SIZE] ; // Buffer qui contiendra la réponse du serveur. | ||
+ | |||
+ | // LiquidCrystal_I2C LCD(0x27,16,2); // Définition de notre écran LCD. | ||
+ | |||
+ | //------------------------------ | ||
+ | // Define hardware type, size, and output pins: | ||
+ | #define HARDWARE_TYPE MD_MAX72XX::FC16_HW | ||
+ | #define MAX_DEVICES 4 | ||
+ | #define CS_PIN D3 | ||
+ | #define DATA_PIN D2 | ||
+ | #define CLK_PIN D1 | ||
+ | |||
+ | // Create a new instance of the MD_Parola class with hardware SPI connection: | ||
+ | // Setup for software SPI: | ||
+ | MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); | ||
+ | |||
+ | //------------------------------- | ||
+ | |||
+ | |||
+ | #define TEN_SECONDS 10000 // On appelera le serveur de données toutes les 10000 ms = 10 secondes. | ||
+ | unsigned long myWakeUp ; // Timer mis en place pour limiter le nombre d'appels au serveur de données. | ||
+ | |||
+ | /* -------------------------------------------------------------------------------------------------------------- | ||
+ | * GetServerResponse() : Envoi requête HTTP au serveur et récupération de la réponse | ||
+ | * valeur de retour : 1 = ok, 0 = pas de connexion serveur, -1 = erreur HTTP GET, -2 = réponse anormale (trop de caractères) | ||
+ | * ------------------------------------------------------------------------------------------------------------- */ | ||
+ | int getServerResponse() { | ||
+ | |||
+ | WiFiClient myWiFiClient; | ||
+ | HTTPClient myHTTPClient; | ||
+ | |||
+ | /* | ||
+ | * Connexion au serveur ... | ||
+ | */ | ||
+ | |||
+ | if (!myHTTPClient.begin(myWiFiClient, GL_HTTP_REQUEST)) { | ||
+ | Serial.printf("[HTTP} Unable to connect\n"); | ||
+ | return(0) ; | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | * Envoi de l'entête HTTP ... | ||
+ | */ | ||
+ | |||
+ | Serial.print("[HTTP] GET...\n"); | ||
+ | int httpCode = myHTTPClient.GET(); | ||
+ | |||
+ | if (httpCode <= 0) { | ||
+ | Serial.printf("[HTTP] GET... failed, error: %s\n", myHTTPClient.errorToString(httpCode).c_str()); | ||
+ | myHTTPClient.end(); | ||
+ | return(-1) ; | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | * Requête HTTP OK ! --> On fait à nouveau clignoter les leds en bleu (3 flashs longs). | ||
+ | */ | ||
+ | |||
+ | /* | ||
+ | * 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 | ||
+ | * qui plante de temps en temps, d'après les forums. De plus, cela permet de faire défiler les leds pour montrer que | ||
+ | * ça suit son cours ... | ||
+ | */ | ||
+ | |||
+ | int myIndex = 0 ; | ||
+ | while(myWiFiClient.available() || myWiFiClient.connected()){ | ||
+ | |||
+ | int myResp = myWiFiClient.read(); | ||
+ | if (myResp == -1) { | ||
+ | break ; | ||
+ | } | ||
+ | |||
+ | GL_httpResponse[myIndex] = myResp; | ||
+ | // Serial.print(GL_httpResponse[myIndex]) ; | ||
+ | if (myIndex++ > MAX_RESPONSE_SIZE) { | ||
+ | Serial.println("Réponse trop longue : " + String(MAX_RESPONSE_SIZE) + "caractères, et ne peut pas être traitée") ; | ||
+ | myWiFiClient.stop(); | ||
+ | return(-2); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | /* | ||
+ | * Tout s'est bien passé --> on passe les leds en vert, et on se déconnecte du serveur. | ||
+ | */ | ||
+ | |||
+ | myHTTPClient.end(); | ||
+ | return(1) ; | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | /* -------------------------------------------------------------------------------------------------------- | ||
+ | * showJSONAnswer : Décodage de la structure de données JSON | ||
+ | * Paramètres : | ||
+ | * - pResponse : endroit se trouve la réponse (au format JSON) du serveur | ||
+ | * - pRespMax : nombre max de caractères autorisés pour la réponse | ||
+ | * -------------------------------------------------------------------------------------------------------- */ | ||
+ | void showJSONAnswer(char *pResponse, int pRespMax) { | ||
+ | |||
+ | // Création de notre structure JSON | ||
+ | // Le besoin en mémoire (capacity) doit être vérifié sur l'assistant https://arduinojson.org/v6/assistant/ | ||
+ | // 1) dans la première page de l'assistant, sélectionnez le processeur (par exemple "ESP8266"), le mode | ||
+ | // "Deserialize", et le type d'entrée "char*", puis cliquez sur le bouton "Netx:JSON" | ||
+ | // 2) Lancez votre requête depuis un navigateur. Dans notre exemple, tapez dans la barre d'adresse : | ||
+ | // "https://data.rennesmetropole.fr/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553" | ||
+ | // 3) Recopiez la réponse obtenue - sous sa forme "Données Brutes" du navigateur vers l'assistant | ||
+ | // 4) L'assistant va alors préconiser le bon objet à créer (StaticJsonDocument ou DynamicJsonDocument), | ||
+ | // ainsi que la taille à réserver. L'assistant va même proposer un exemple de programme exploitant toutes | ||
+ | // les informations de la structure JSON. | ||
+ | // Pour notre exemple, l'assistant a proposé la définition qui suit. | ||
+ | |||
+ | StaticJsonDocument<300> doc; | ||
+ | |||
+ | // Décodage de la réponse JSON. | ||
+ | // La fonction deserializeJson va transformer la réponse "texte" du serveur, en une structure de données recopiée | ||
+ | // dans la variable 'doc', où il sera ensuite facile d'aller chercher les informations souhaitées. | ||
+ | |||
+ | DeserializationError error = deserializeJson(doc, pResponse, pRespMax); | ||
+ | if (error) { | ||
+ | Serial.println("--- Décodage réponse JSON KO, code " + String(error.f_str())) ; | ||
+ | return; | ||
+ | } | ||
+ | Serial.println("--- Décodage réponse JSON OK !") ; | ||
+ | |||
+ | // Nous pouvons maintenant extraire facilement les informations qui nous intéressent, | ||
+ | // en n'oubliant pas le niveau de profondeur de la donnée au sein de la structure JSON. | ||
+ | // Ce niveau de profondeur est incrémenté par le nombre de '{' ou '[' rencontrés, et | ||
+ | // décrémenté lors de la rencontre des ']' et {}'. Sur notre exemple 'rocade de Rennes', | ||
+ | // cela donne ceci : | ||
+ | // +-----------------------------------------------------------------+ | ||
+ | // | { | ... Entrée niveau 1 | ||
+ | // | "nhits": 1, | | ||
+ | // | "parameters": { | ... Entrée niveau 2 | ||
+ | // | "dataset": "etat-du-trafic-en-temps-reel", | | ||
+ | // | (...) | | ||
+ | // | }, | ... Retour niveau 1 | ||
+ | // | "records": [ | ... Début d'un tableau : niveau 2 | ||
+ | // | { | ... Entrée niveau 3 | ||
+ | // | (...) | | | ||
+ | // | "fields": { | ... Entrée niveau 4 | ||
+ | // | (...) | | ||
+ | // | "averagevehiclespeed": 88, | | ||
+ | // | (...) | | ||
+ | // | "datetime": "2022-11-30T11:57:00+01:00", | | ||
+ | // +-----------------------------------------------------------------+ | ||
+ | // ... et donc : | ||
+ | // - (1er niveau) --------- doc["nhits"] donnera la valeur 1, | ||
+ | // - (2ème niveau) -------- doc["parameters"]["dataset"] donnera la valeur "etat-du-trafic-en-temps-reel" | ||
+ | // - (4ème niveau) -------- doc["records"][0]["fields"]["averagevehiclespeed"] donnera la valeur 87 | ||
+ | |||
+ | // Extraction et affichage sur le port série de trois valeurs | ||
+ | |||
+ | char myMessage[80]; | ||
+ | for (int i = 0; i < 5; i++) { | ||
+ | const char* Title = doc[i] ; | ||
+ | Serial.println("Trend n° " + String(i) + " : " + String(Title)) ; | ||
+ | // LCD.setCursor(1, 0); | ||
+ | sprintf(myMessage, "Trends n° %d - %s", i, Title) ; | ||
+ | // LCD.print(myMessage); | ||
+ | |||
+ | |||
+ | myDisplay.print(myMessage); | ||
+ | |||
+ | |||
+ | delay(500) ; | ||
+ | // LCD.clear() ; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | /* -------------------------------------------------------------------------------------------------------- | ||
+ | * SETUP : Initialisation | ||
+ | * -------------------------------------------------------------------------------------------------------- */ | ||
+ | void setup() { | ||
+ | |||
+ | // Initialisation de la liaison série, affichage 1er message | ||
+ | |||
+ | Serial.begin(115200); | ||
+ | delay(100) ; | ||
+ | Serial.println(); | ||
+ | Serial.println("-----------------------") ; | ||
+ | Serial.println("Exemple extraction JSON") ; | ||
+ | Serial.println("-----------------------") ; | ||
+ | |||
+ | // Initialisation de l'écran LCD. | ||
+ | |||
+ | // LCD.init(); // initialisation de l'afficheur | ||
+ | // LCD.backlight(); | ||
+ | |||
+ | myDisplay.begin(); | ||
+ | myDisplay.displayClear(); | ||
+ | |||
+ | // Tentative de connexion au Wi-Fi. Si la carte n'a pas réussi se connecter au dernier Point d'Accès connu, | ||
+ | // alors elle va se positionner en mode Point d'Accès, demandera sur l'adresse 192.168.4.1 quel nouveau | ||
+ | // Point d'Accès choisir. Par défaut, on restera bloqué tant que l'utilisateur n'aura pas fait de choix. | ||
+ | |||
+ | Serial.println("Connexion au Wi-Fi ..."); | ||
+ | // LCD.print("Connexion au Wi-Fi ...") ; | ||
+ | myDisplay.print("Connexion au Wi-Fi ..."); | ||
+ | if (myWiFiManager.autoConnect(mySSID, mySecKey)) { | ||
+ | Serial.println(); Serial.print("Connecté ! Adresse IP : "); | ||
+ | // LCD.print("Connecté ! Adresse IP : ") ; LCD.print(WiFi.localIP()) ; | ||
+ | Serial.println(WiFi.localIP()); | ||
+ | } | ||
+ | else { | ||
+ | Serial.println("Connexion Wi-Fi KO :-("); | ||
+ | } | ||
+ | |||
+ | // Initialisation du timer qui sera testé dans loop() - pour faire appel au serveur seulement toutes les 10 secondes | ||
+ | // millis() est une fonction système donnant le nombre de ms depuis le lancement ou la réinitialisation de la carte. | ||
+ | |||
+ | unsigned long myWakeUp = millis() + TEN_SECONDS ; | ||
+ | |||
+ | } | ||
+ | |||
+ | /* -------------------------------------------------------------------------------------------------------------- | ||
+ | * LOOP : fonction appelée régulièrement par le système | ||
+ | * ------------------------------------------------------------------------------------------------------------- */ | ||
+ | void loop() { | ||
+ | |||
+ | unsigned long myNow = millis() ; | ||
+ | if (myNow >= myWakeUp) { | ||
+ | Serial.println("Wake Up ! Nouvelle demande au serveur ...") ; | ||
+ | if (getServerResponse() == 1) { | ||
+ | Serial.println("Réponse reçue du serveur, lancement analyse JSON ...") ; | ||
+ | showJSONAnswer(GL_httpResponse, MAX_RESPONSE_SIZE) ; | ||
+ | } | ||
+ | myWakeUp = myNow + TEN_SECONDS ; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
[[Catégorie:POCL]] | [[Catégorie:POCL]] | ||
+ | [[Catégorie:Hackathon POCL 2022]] | ||
+ | |||
+ | [[Catégorie:Arduino]] |
Version actuelle datée du 15 janvier 2024 à 16:03
Sommaire
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
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.
Bouton à imprimer en 3D
Fichier:POCL2022-BoutonChampi.stl
Découpe Laser de la boite
QR-Code pour se rendre sur le site web
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 }