*'''Episode 4 :''' [https://www.wikidebrouillard.org/wiki/Code_Minimal_R%C3%A9seau_-_(4)_Mes_Wemos_D1_Mini_discutent_sur_Internet_avec_MQTT Mon Wemos D1 mini discute sur Internet avec MQTT.]
+, <nowiki>Nous allons maintenant nous intéresser à la récupération de données sur Internet (informations sur la météo, sur la pollution, sur les derniers recensements, ...). De nombreux serveurs de données, et en particulier les serveurs "Open Data" (offrant des données libres de droit), sont accessibles en mode web. C'est-à-dire qu'une simple requête dans la barre d'adresse de votre navigateur, permet de récupérer les informations souhaitées.<br /><br /><br /><br />Et, encore mieux, dans la plupart des cas, la réponse revient sous une forme standardisée de type '''JSON''' (JavaScript Objet Notation), que les navigateurs récents sont capables de décoder. A titre d'exemple, ouvrez un nouvel onglet dans votre navigateur, et recopiez dans la barre d'adresse ce qui suit ...<br /><br /><br/><br /> https://data.rennesmetropole.fr/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=rocade<br />... et vous devriez avoir en retour un texte de ce type : <br /> <br /> {"nhits": 63, "parameters": {"dataset": "etat-du-trafic-en-temps-reel", "q": "rocade", "rows": 10, "start": 0, "format": "json", "timezone": "UTC"}, "records": [{"datasetid": "etat-du-trafic-en-temps-reel", "recordid": "c8cd4fc9d2a9f1840170322c834f827fc100cc75", "fields": {"traveltimereliability": 100, "traveltime": 55, "predefinedlocationreference": "30023", "averagevehiclespeed": 91, "datetime": "2022-11-29T15:11:00+01:00", "gml_id": "v_rva_troncon_fcd.fid-722fb9f8_184c264cda5_453f", "trafficstatus": "freeFlow", "func_class": 666, "geo_point_2d": [48.14130932076887, -1.6781068587055177], '''(...)'''<br /><br />... mais que votre navigateur va quasi-immédiatement immédiatement reconnaître comme un format JSON, et afficher sous une forme plus structurée :<br /><br /><div class="annotatedImageDiv" typeof="Image" data-resource="Fichier:Code minimal des fonctions reseau Code JSON.png" data-sourceimage="https://www.wikidebrouillard.org/images/d/d7/Code_minimal_des_fonctions_reseau_Code_JSON.png"><span ><div class="center"><div class="floatnone"><a href="/wiki/Fichier:Code_minimal_des_fonctions_reseau_Code_JSON.png" class="image" title="Exemple de réponse JSON"><img alt="Exemple de réponse JSON" src="/images/d/d7/Code_minimal_des_fonctions_reseau_Code_JSON.png" width="564" height="472" data-file-width="564" data-file-height="472" /></a></div></div></span></div><br /><br />Nous avons fait ici appel au serveur Open Data de la ville de Rennes, et avons fait une requête demandant l'état du trafic sur la rocade principale. Ce même serveur propose un tas d'autres données libres, et on peut trouver sur Internet une multitude d'autres serveurs "Open Data" en mode JSON.</nowiki> , <nowiki>... bon, ok, mais mon D1 mini n'a pas de navigateur ?<br /><br /><br />C'est là où deux bibliothèques vont nous être utiles : <br /><br />*la première pour permettre à notre carte se connecter au serveur de données en mode sécurisé (car la plupart des sites web ont une adresse ''''https'''://www...') : '''WiFiClientSecure'''. Celle-ci est intégrée de base dans l'environnement de développement Arduino.<br />*la seconde pour décoder le format JSON et extraire facilement les éléments de réponse qui nous intéressent : '''ArduinoJson'''. Celle-ci doit être récupérée dans le gestionnaire de bibliothèques :<br/> <div class="annotatedImageDiv" typeof="Image" data-resource="Fichier:Code minimal des fonctions reseau Biblio ArduinoJSON.png" data-sourceimage="https://www.wikidebrouillard.org/images/9/92/Code_minimal_des_fonctions_reseau_Biblio_ArduinoJSON.png"><span ><div class="center"><div class="floatnone"><a href="/wiki/Fichier:Code_minimal_des_fonctions_reseau_Biblio_ArduinoJSON.png" class="image" title="Bibliothèque ArduinoJSON"><img alt="Bibliothèque ArduinoJSON" src="/images/9/92/Code_minimal_des_fonctions_reseau_Biblio_ArduinoJSON.png" width="752" height="107" data-file-width="752" data-file-height="107" /></a></div></div></span></div><br /><br />Les possibilités sont multiples, et l'exploitation des données JSON par les cartes D1 mini ou ESP32, peut prendre des formes très sympathiques : voir par exemple les réalisations "[https://www.wiki.lesfabriquesduponant.net/index.php?title=POCL_:_VOIR_DEMAIN Voir Demain]" et "[https://www.wiki.lesfabriquesduponant.net/index.php?title=POCL_:_Hawaiiiii Hawaiiiii]" issues d'un hackathon organisé en décembre 2021 par Les Petits Débrouillards Grand Ouest et L'Edulab de l'Université de Rennes 2.<br /><br/><div class="annotatedImageDiv" typeof="Image" data-resource="Fichier:Code minimal des fonctions reseau Copie tableau mode img cause bug si tableau wiki.png" data-sourceimage="https://www.wikidebrouillard.org/images/9/94/Code_minimal_des_fonctions_reseau_Copie_tableau_mode_img_cause_bug_si_tableau_wiki.png"><span ><a href="/wiki/Fichier:Code_minimal_des_fonctions_reseau_Copie_tableau_mode_img_cause_bug_si_tableau_wiki.png" class="image" title="Fonctions JSON"><img alt="Fonctions JSON" src="/images/thumb/9/94/Code_minimal_des_fonctions_reseau_Copie_tableau_mode_img_cause_bug_si_tableau_wiki.png/800px-Code_minimal_des_fonctions_reseau_Copie_tableau_mode_img_cause_bug_si_tableau_wiki.png" width="800" height="191" data-file-width="1293" data-file-height="309" /></a></span></div><br /><br /><br />Pour connaître toutes les autres possibilités de cette bibliothèque, voir sa référence, [https://github.com/256dpi/arduino-mqtt ici].<br /><br /><br />'''Code minimal :'''<br /><br /><br />Bon, en fait, pas tout à fait "minimal" : <br /><br />*pour des raisons de clarté, nous avons défini deux fonctions : '''serverRequest''' pour générer la requête auprès du serveur et récupérer la réponse, et '''showJSONAnswer''' pour analyser la réponse (décodage des informations JSON).<br />*pour faciliter la réutilisation de ce code, plutôt que de tout traiter dans le setup(), nous activerons ces fonctions régulièrement, depuis la boucle loop(), ce qui est le mode de fonctionnement habituel.<br /><br /><br/><div class="mw-highlight mw-content-ltr" dir="ltr"><pre><span></span><span class="lineno"> 1 </span><span class="cm">/* =========================================================================================================</span><br /><span class="lineno"> 2 </span><span class="cm"> * </span><br /><span class="lineno"> 3 </span><span class="cm"> * CODE MINIMAL RESEAU - ETAPE 5 : Données JSON</span><br /><span class="lineno"> 4 </span><span class="cm"> * </span><br /><span class="lineno"> 5 </span><span class="cm"> * ---------------------------------------------------------------------------------------------------------</span><br /><span class="lineno"> 6 </span><span class="cm"> * Les petits Débrouillards - décembre 2022 - CC-By-Sa http://creativecommons.org/licenses/by-nc-sa/3.0/</span><br /><span class="lineno"> 7 </span><span class="cm"> * ========================================================================================================= */</span><br /><span class="lineno"> 8 </span><br /><span class="lineno"> 9 </span><span class="c1">// Bibliothèques requises</span><br /><span class="lineno"> 10 </span><span class="c1">// ATTENTION AUX MAJUSCULES & MINUSCULES ! Sinon d'autres bibliothèques, plus ou moins valides, seraient utilisées.</span><br /><span class="lineno"> 11 </span><br /><span class="lineno"> 12 </span><span class="cp">#include</span> <span class="cpf"><WiFiManager.h> // Gestion de la connexion Wi-Fi (recherche de points d'accès) </span><span class="cp"></span><br /><span class="lineno"> 13 </span><span class="cp">#include</span> <span class="cpf"><WiFiClientSecure.h> // Gestion de la connexion (HTTP) à un serveur de données</span><span class="cp"></span><br /><span class="lineno"> 14 </span><span class="cp">#include</span> <span class="cpf"><ArduinoJson.h> // Fonctions de décodage JSON des réponses du serveur. </span><span class="cp"></span><br /><span class="lineno"> 15 </span><br /><span class="lineno"> 16 </span><br /><span class="lineno"> 17 </span><br /><span class="lineno"> 18 </span><span class="c1">// Variables globales</span><br /><span class="lineno"> 19 </span><br /><span class="lineno"> 20 </span><span class="n">WiFiManager</span> <span class="n">myWiFiManager</span><span class="p">;</span> <span class="c1">// Création de mon instance de WiFiManager.</span><br /><span class="lineno"> 21 </span><span class="n">WiFiClientSecure</span> <span class="n">myWiFiClient</span><span class="p">;</span> <span class="c1">// Création de mon instance de client WiFi.</span><br /><span class="lineno"> 22 </span><span class="kr">const</span> <span class="kr">char</span><span class="o">*</span> <span class="n">mySSID</span> <span class="o">=</span> <span class="s">"AP_PetitDeb"</span> <span class="p">;</span> <span class="c1">// Nom de la carte en mode Point d'Accès.</span><br /><span class="lineno"> 23 </span><span class="kr">const</span> <span class="kr">char</span><span class="o">*</span> <span class="n">mySecKey</span> <span class="o">=</span> <span class="s">"PSWD1234"</span> <span class="p">;</span> <span class="c1">// Mot de passe associé, 8 caractères au minimum.</span><br /><span class="lineno"> 24 </span><br /><span class="lineno"> 25 </span><span class="kr">char</span><span class="o">*</span> <span class="n">Data_HOST</span> <span class="o">=</span> <span class="s">"data.rennesmetropole.fr"</span><span class="p">;</span> <span class="c1">// Serveur web hébergeant les données qui nous intéressent</span><br /><span class="lineno"> 26 </span><span class="kr">int</span> <span class="n">Data_PORT</span> <span class="o">=</span> <span class="mi">443</span><span class="p">;</span> <span class="c1">// Port sur lequel envoyer la requête</span><br /><span class="lineno"> 27 </span><span class="kr">char</span><span class="o">*</span> <span class="n">Data_REQUEST</span> <span class="o">=</span> <span class="c1">// Requête (sur cet exemple : demande de l'état du trafic au point</span><br /><span class="lineno"> 28 </span> <span class="c1">// 31553, correspondant à la porte de Saint-Malo de la rocade de Rennes </span><br /><span class="lineno"> 29 </span> <span class="s">"/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553"</span><span class="p">;</span> <br /><span class="lineno"> 30 </span><br /><span class="lineno"> 31 </span><br /><span class="lineno"> 32 </span><span class="kr">const</span> <span class="kr">int</span> <span class="n">MAX_RESPONSE_SIZE</span> <span class="o">=</span> <span class="mi">6000</span> <span class="p">;</span> <span class="c1">// Taille max de la réponse attendue d'un serveur. A modifier en fonction du besoin.</span><br /><span class="lineno"> 33 </span><span class="kr">char</span> <span class="n">Data_Response</span><span class="p">[</span><span class="n">MAX_RESPONSE_SIZE</span><span class="p">]</span> <span class="p">;</span> <span class="c1">// Buffer qui contiendra la réponse du serveur.</span><br /><span class="lineno"> 34 </span> <br /><span class="lineno"> 35 </span><span class="cp">#define TEN_SECONDS 10000 </span><span class="c1">// On appelera le serveur de données toutes les 10000 ms = 10 secondes.</span><br /><span class="lineno"> 36 </span><span class="kr">unsigned</span> <span class="kr">long</span> <span class="n">myWakeUp</span> <span class="p">;</span> <span class="c1">// Timer mis en place pour limiter le nombre d'appels au serveur de données.</span><br /><span class="lineno"> 37 </span><br /><span class="lineno"> 38 </span><span class="cm">/* --------------------------------------------------------------------------------------------------------------</span><br /><span class="lineno"> 39 </span><span class="cm"> * serverRequest() : Envoi requête HTTP au serveur et récupération de la réponse</span><br /><span class="lineno"> 40 </span><span class="cm"> * paramètres : </span><br /><span class="lineno"> 41 </span><span class="cm"> * - pHost : nom du serveur ; </span><br /><span class="lineno"> 42 </span><span class="cm"> * - pPort : port sur lequel est appelé le serveur ; </span><br /><span class="lineno"> 43 </span><span class="cm"> * - pRequest : requête au serveur.</span><br /><span class="lineno"> 44 </span><span class="cm"> * - pResponse : endroit où stocker la réponse</span><br /><span class="lineno"> 45 </span><span class="cm"> * - pRespMax : nombre max de caractères autorisés pour la réponse</span><br /><span class="lineno"> 46 </span><span class="cm"> * valeur de retour : </span><br /><span class="lineno"> 47 </span><span class="cm"> * -2 = réponse tronquée (trop de caractères) ;</span><br /><span class="lineno"> 48 </span><span class="cm"> * -1 = pas de réponse ;</span><br /><span class="lineno"> 49 </span><span class="cm"> * 0 = pas de connexion au serveur ;</span><br /><span class="lineno"> 50 </span><span class="cm"> * 1 = ok.</span><br /><span class="lineno"> 51 </span><span class="cm"> * ------------------------------------------------------------------------------------------------------------- */</span><br /><span class="lineno"> 52 </span><span class="kr">int</span> <span class="nf">serverRequest</span><span class="p">(</span><span class="kr">char</span><span class="o">*</span> <span class="n">pHost</span><span class="p">,</span> <span class="kr">int</span> <span class="n">pPort</span><span class="p">,</span> <span class="kr">char</span><span class="o">*</span> <span class="n">pRequest</span><span class="p">,</span> <span class="kr">char</span> <span class="o">*</span><span class="n">pResponse</span><span class="p">,</span> <span class="kr">int</span> <span class="n">pRespMax</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno"> 53 </span> <br /><span class="lineno"> 54 </span> <span class="kr">const</span> <span class="kr">int</span> <span class="n">API_TIMEOUT</span> <span class="o">=</span> <span class="mi">15000</span><span class="p">;</span> <span class="c1">// Pour être sûr de recevoir l'en-tête de la réponse client.</span><br /><span class="lineno"> 55 </span><br /><span class="lineno"> 56 </span> <span class="c1">// Comme la connexion est sécurisée (protocole HTTPS), il faudrait indiquer le certificat du site web.</span><br /><span class="lineno"> 57 </span> <span class="c1">// Pour simplifier, on va utiliser l'option magique ".setInsecure()", ce qui n'est pas important dans </span><br /><span class="lineno"> 58 </span> <span class="c1">// notre exemple, où les données échangées ne sont pas confidentielles.</span><br /><span class="lineno"> 59 </span><br /><span class="lineno"> 60 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="n">setInsecure</span><span class="p">();</span><br /><span class="lineno"> 61 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">setTimeout</span><span class="p">(</span><span class="n">API_TIMEOUT</span><span class="p">);</span><br /><span class="lineno"> 62 </span><br /><span class="lineno"> 63 </span> <span class="c1">// Connexion au serveur (on essaie 5 fois, avec un intervalle d'une seconde)</span><br /><span class="lineno"> 64 </span><br /><span class="lineno"> 65 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"--- Connexion au serveur ["</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">pHost</span><span class="p">)</span> <span class="o">+</span> <span class="s">"] "</span><span class="p">);</span> <br /><span class="lineno"> 66 </span> <span class="kr">int</span> <span class="n">nbTries</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><br /><span class="lineno"> 67 </span> <span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">connect</span><span class="p">(</span><span class="n">pHost</span><span class="p">,</span> <span class="n">pPort</span><span class="p">))</span> <span class="p">{</span><br /><span class="lineno"> 68 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"."</span><span class="p">);</span><br /><span class="lineno"> 69 </span> <span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="n">nbTries</span> <span class="o">></span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno"> 70 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Connexion impossible :-("</span><span class="p">);</span><br /><span class="lineno"> 71 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">stop</span><span class="p">();</span><br /><span class="lineno"> 72 </span> <span class="k">return</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><br /><span class="lineno"> 73 </span> <span class="p">}</span><br /><span class="lineno"> 74 </span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span><br /><span class="lineno"> 75 </span> <span class="p">}</span> <br /><span class="lineno"> 76 </span><br /><span class="lineno"> 77 </span> <span class="c1">// Connecté à notre serveur ! --> Envoi de la requête URL. Il faut envoyer en fait une suite de lignes : </span><br /><span class="lineno"> 78 </span> <span class="c1">// "GET <notre requête> HTTP/1.1"</span><br /><span class="lineno"> 79 </span> <span class="c1">// "Host: <nom du serveur>"</span><br /><span class="lineno"> 80 </span> <span class="c1">// "Connection: close"</span><br /><span class="lineno"> 81 </span> <span class="c1">// <ligne vide></span><br /><span class="lineno"> 82 </span> <span class="c1">// Cet envoi se fait simplement grâce à la fonction println du client WiFi, similaire à celle que </span><br /><span class="lineno"> 83 </span> <span class="c1">// l'on utilise pour envoyer des données au moniteur série pour nos traces.</span><br /><span class="lineno"> 84 </span><br /><span class="lineno"> 85 </span> <span class="kr">String</span> <span class="n">myURL</span> <span class="o">=</span> <span class="kr">String</span><span class="p">(</span><span class="n">pRequest</span><span class="p">);</span><br /><span class="lineno"> 86 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">()</span> <span class="p">;</span> <br /><span class="lineno"> 87 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Connexion OK ! --> Envoi requête URL - "</span> <span class="o">+</span> <span class="n">myURL</span><span class="p">);</span><br /><span class="lineno"> 88 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"GET "</span> <span class="o">+</span> <span class="n">myURL</span> <span class="o">+</span> <span class="s">" HTTP/1.1"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno"> 89 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Host: "</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">pHost</span><span class="p">))</span> <span class="p">;</span><br /><span class="lineno"> 90 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Connection: close"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno"> 91 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">println</span><span class="p">()</span> <span class="p">;</span><br /><span class="lineno"> 92 </span> <br /><span class="lineno"> 93 </span> <span class="c1">// Attente de la réponse ....(on essaie 50 fois, avec un intervalle de 100ms, donc 5 secondes en tout)</span><br /><span class="lineno"> 94 </span> <br /><span class="lineno"> 95 </span> <span class="n">nbTries</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span><br /><span class="lineno"> 96 </span> <span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">available</span><span class="p">()){</span><br /><span class="lineno"> 97 </span> <span class="k">if</span> <span class="p">(</span><span class="o">++</span><span class="n">nbTries</span> <span class="o">></span> <span class="mi">50</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno"> 98 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Pas de réponse :-("</span><span class="p">);</span><br /><span class="lineno"> 99 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">stop</span><span class="p">();</span><br /><span class="lineno">100 </span> <span class="k">return</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span><br /><span class="lineno">101 </span> <span class="p">}</span><br /><span class="lineno">102 </span> <span class="nf">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span><br /><span class="lineno">103 </span> <span class="p">}</span><br /><span class="lineno">104 </span><br /><span class="lineno">105 </span> <span class="c1">// Récupération de l'en-tête de la réponse (dont on ne fera rien)</span><br /><span class="lineno">106 </span> <span class="c1">// Cette entête est une suite de caractères, composant un certain nombre de lignes (ie se terminant par '\n'), </span><br /><span class="lineno">107 </span> <span class="c1">// la dernière ligne de l'entête n'est composée que du caractère "\r" (suivie du '\n') ;</span><br /><span class="lineno">108 </span> <br /><span class="lineno">109 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Réponse OK --> Récupération de l'en-tête ..."</span><span class="p">);</span><br /><span class="lineno">110 </span> <span class="kr">String</span> <span class="n">myLine</span> <span class="p">;</span><br /><span class="lineno">111 </span> <span class="k">while</span> <span class="p">(</span><span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">available</span><span class="p">())</span> <span class="p">{</span><br /><span class="lineno">112 </span> <span class="n">myLine</span> <span class="o">=</span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">readStringUntil</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">);</span><br /><span class="lineno">113 </span> <span class="k">if</span> <span class="p">(</span><span class="n">myLine</span> <span class="o">==</span> <span class="s">"</span><span class="se">\r</span><span class="s">"</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">114 </span> <span class="k">break</span><span class="p">;</span><br /><span class="lineno">115 </span> <span class="p">}</span><br /><span class="lineno">116 </span> <span class="p">}</span><br /><span class="lineno">117 </span><br /><span class="lineno">118 </span> <span class="c1">// Entête reçue ! On va alors recopier dans pResponse tous les caractères qui suivent </span><br /><span class="lineno">119 </span> <span class="c1">// en faisant attention à ne pas dépasser la taille du buffer.</span><br /><span class="lineno">120 </span><br /><span class="lineno">121 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Entête ok --> Récupération des données ..."</span><span class="p">);</span><br /><span class="lineno">122 </span> <span class="kr">int</span> <span class="n">myIndex</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">;</span><br /><span class="lineno">123 </span> <span class="k">while</span> <span class="p">(</span><span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">available</span><span class="p">())</span> <span class="p">{</span><br /><span class="lineno">124 </span><br /><span class="lineno">125 </span> <span class="kr">char</span> <span class="n">myResp</span> <span class="o">=</span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">read</span><span class="p">();</span><br /><span class="lineno">126 </span> <span class="cm">/* Debug supprimé ... Serial.println(myResp) ; */</span><br /><span class="lineno">127 </span> <span class="n">pResponse</span><span class="p">[</span><span class="n">myIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">myResp</span><span class="p">;</span> <br /><span class="lineno">128 </span> <span class="k">if</span> <span class="p">(</span><span class="n">myIndex</span><span class="o">++</span> <span class="o">>=</span> <span class="n">pRespMax</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">129 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"*** Réponse trop longue : "</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">pRespMax</span><span class="p">)</span> <span class="o">+</span> <span class="s">"caractères, et ne peut pas être traitée"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">130 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">stop</span><span class="p">();</span><br /><span class="lineno">131 </span> <span class="k">return</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">);</span><br /><span class="lineno">132 </span> <span class="p">}</span><br /><span class="lineno">133 </span> <span class="n">pResponse</span><span class="p">[</span><span class="n">myIndex</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span> <span class="c1">// Vu sur forums : conseillé d'ajouté 'fin de chaîne' systématiquement</span><br /><span class="lineno">134 </span> <span class="nf">delay</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">;</span> <span class="c1">// Et également d'ajouter ce tout petit délai pour éviter des plantages.</span><br /><span class="lineno">135 </span> <br /><span class="lineno">136 </span> <span class="p">}</span><br /><span class="lineno">137 </span><br /><span class="lineno">138 </span> <span class="c1">// Tout s'est bien passé ! On arrête notre client WiFi</span><br /><span class="lineno">139 </span><br /><span class="lineno">140 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Récupération des données ok ("</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">myIndex</span><span class="p">)</span> <span class="o">+</span> <span class="s">" caractères)."</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">141 </span> <span class="n">myWiFiClient</span><span class="p">.</span><span class="nf">stop</span><span class="p">();</span><br /><span class="lineno">142 </span> <span class="k">return</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">143 </span><br /><span class="lineno">144 </span><span class="p">}</span><br /><span class="lineno">145 </span><br /><span class="lineno">146 </span><span class="cm">/* --------------------------------------------------------------------------------------------------------</span><br /><span class="lineno">147 </span><span class="cm"> * showJSONAnswer : Décodage de la structure de données JSON</span><br /><span class="lineno">148 </span><span class="cm"> * Paramètres :</span><br /><span class="lineno">149 </span><span class="cm"> * - pResponse : endroit se trouve la réponse (au format JSON) du serveur</span><br /><span class="lineno">150 </span><span class="cm"> * - pRespMax : nombre max de caractères autorisés pour la réponse</span><br /><span class="lineno">151 </span><span class="cm"> * -------------------------------------------------------------------------------------------------------- */</span><br /><span class="lineno">152 </span><span class="kr">void</span> <span class="nf">showJSONAnswer</span><span class="p">(</span><span class="kr">char</span> <span class="o">*</span><span class="n">pResponse</span><span class="p">,</span> <span class="kr">int</span> <span class="n">pRespMax</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">153 </span><br /><span class="lineno">154 </span> <span class="c1">// Création de notre structure JSON</span><br /><span class="lineno">155 </span> <span class="c1">// Le besoin en mémoire (capacity) doit être vérifié sur l'assistant https://arduinojson.org/v6/assistant/</span><br /><span class="lineno">156 </span> <span class="c1">// 1) dans la première page de l'assistant, sélectionnez le processeur (par exemple "ESP8266"), le mode</span><br /><span class="lineno">157 </span> <span class="c1">// "Deserialize", et le type d'entrée "char*", puis cliquez sur le bouton "Netx:JSON"</span><br /><span class="lineno">158 </span> <span class="c1">// 2) Lancez votre requête depuis un navigateur. Dans notre exemple, tapez dans la barre d'adresse :</span><br /><span class="lineno">159 </span> <span class="c1">// "https://data.rennesmetropole.fr/api/records/1.0/search/?dataset=etat-du-trafic-en-temps-reel&q=31553"</span><br /><span class="lineno">160 </span> <span class="c1">// 3) Recopiez la réponse obtenue - sous sa forme "Données Brutes" du navigateur vers l'assistant</span><br /><span class="lineno">161 </span> <span class="c1">// 4) L'assistant va alors préconiser le bon objet à créer (StaticJsonDocument ou DynamicJsonDocument),</span><br /><span class="lineno">162 </span> <span class="c1">// ainsi que la taille à réserver. L'assistant va même proposer un exemple de programme exploitant toutes </span><br /><span class="lineno">163 </span> <span class="c1">// les informations de la structure JSON. </span><br /><span class="lineno">164 </span> <span class="c1">// Pour notre exemple, l'assistant a proposé la définition qui suit.</span><br /><span class="lineno">165 </span> <br /><span class="lineno">166 </span> <span class="n">StaticJsonDocument</span><span class="o"><</span><span class="mi">1024</span><span class="o">></span> <span class="n">doc</span><span class="p">;</span><br /><span class="lineno">167 </span><br /><span class="lineno">168 </span> <span class="c1">// Décodage de la réponse JSON.</span><br /><span class="lineno">169 </span> <span class="c1">// La fonction deserializeJson va transformer la réponse "texte" du serveur, en une structure de données recopiée</span><br /><span class="lineno">170 </span> <span class="c1">// dans la variable 'doc', où il sera ensuite facile d'aller chercher les informations souhaitées.</span><br /><span class="lineno">171 </span> <br /><span class="lineno">172 </span> <span class="n">DeserializationError</span> <span class="n">error</span> <span class="o">=</span> <span class="n">deserializeJson</span><span class="p">(</span><span class="n">doc</span><span class="p">,</span> <span class="n">pResponse</span><span class="p">,</span> <span class="n">pRespMax</span><span class="p">);</span><br /><span class="lineno">173 </span> <span class="k">if</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">174 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Décodage réponse JSON KO, code "</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">error</span><span class="p">.</span><span class="n">f_str</span><span class="p">()))</span> <span class="p">;</span><br /><span class="lineno">175 </span> <span class="k">return</span><span class="p">;</span><br /><span class="lineno">176 </span> <span class="p">}</span><br /><span class="lineno">177 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"--- Décodage réponse JSON OK !"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">178 </span><br /><span class="lineno">179 </span> <span class="c1">// Nous pouvons maintenant extraire facilement les informations qui nous intéressent,</span><br /><span class="lineno">180 </span> <span class="c1">// en n'oubliant pas le niveau de profondeur de la donnée au sein de la structure JSON. </span><br /><span class="lineno">181 </span> <span class="c1">// Ce niveau de profondeur est incrémenté par le nombre de '{' ou '[' rencontrés, et </span><br /><span class="lineno">182 </span> <span class="c1">// décrémenté lors de la rencontre des ']' et {}'. Sur notre exemple 'rocade de Rennes',</span><br /><span class="lineno">183 </span> <span class="c1">// cela donne ceci :</span><br /><span class="lineno">184 </span> <span class="c1">// +-----------------------------------------------------------------+</span><br /><span class="lineno">185 </span> <span class="c1">// | { | ... Entrée niveau 1</span><br /><span class="lineno">186 </span> <span class="c1">// | "nhits": 1, |</span><br /><span class="lineno">187 </span> <span class="c1">// | "parameters": { | ... Entrée niveau 2</span><br /><span class="lineno">188 </span> <span class="c1">// | "dataset": "etat-du-trafic-en-temps-reel", |</span><br /><span class="lineno">189 </span> <span class="c1">// | (...) |</span><br /><span class="lineno">190 </span> <span class="c1">// | }, | ... Retour niveau 1</span><br /><span class="lineno">191 </span> <span class="c1">// | "records": [ | ... Début d'un tableau : niveau 2</span><br /><span class="lineno">192 </span> <span class="c1">// | { | ... Entrée niveau 3</span><br /><span class="lineno">193 </span> <span class="c1">// | (...) | |</span><br /><span class="lineno">194 </span> <span class="c1">// | "fields": { | ... Entrée niveau 4</span><br /><span class="lineno">195 </span> <span class="c1">// | (...) |</span><br /><span class="lineno">196 </span> <span class="c1">// | "averagevehiclespeed": 88, |</span><br /><span class="lineno">197 </span> <span class="c1">// | (...) |</span><br /><span class="lineno">198 </span> <span class="c1">// | "datetime": "2022-11-30T11:57:00+01:00", |</span><br /><span class="lineno">199 </span> <span class="c1">// +-----------------------------------------------------------------+</span><br /><span class="lineno">200 </span> <span class="c1">// ... et donc :</span><br /><span class="lineno">201 </span> <span class="c1">// - (1er niveau) --------- doc["nhits"] donnera la valeur 1,</span><br /><span class="lineno">202 </span> <span class="c1">// - (2ème niveau) -------- doc["parameters"]["dataset"] donnera la valeur "etat-du-trafic-en-temps-reel"</span><br /><span class="lineno">203 </span> <span class="c1">// - (4ème niveau) -------- doc["records"][0]["fields"]["averagevehiclespeed"] donnera la valeur 87</span><br /><span class="lineno">204 </span><br /><span class="lineno">205 </span> <span class="c1">// Extraction et affichage sur le port série de trois valeurs</span><br /><span class="lineno">206 </span><br /><span class="lineno">207 </span> <span class="kr">String</span> <span class="n">myLocRef</span> <span class="o">=</span> <span class="kr">String</span><span class="p">(</span><span class="n">doc</span><span class="p">[</span><span class="s">"records"</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">"fields"</span><span class="p">][</span><span class="s">"predefinedlocationreference"</span><span class="p">])</span> <span class="p">;</span><br /><span class="lineno">208 </span> <span class="kr">String</span> <span class="n">myTime</span> <span class="o">=</span> <span class="kr">String</span><span class="p">(</span><span class="n">doc</span><span class="p">[</span><span class="s">"records"</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">"fields"</span><span class="p">][</span><span class="s">"datetime"</span><span class="p">])</span> <span class="p">;</span><br /><span class="lineno">209 </span> <span class="kr">int</span> <span class="n">mySpeed</span> <span class="o">=</span> <span class="n">doc</span><span class="p">[</span><span class="s">"records"</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">"fields"</span><span class="p">][</span><span class="s">"averagevehiclespeed"</span><span class="p">]</span> <span class="p">;</span><br /><span class="lineno">210 </span> <br /><span class="lineno">211 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"Vitesse au point "</span> <span class="o">+</span> <span class="n">myLocRef</span> <span class="o">+</span> <span class="s">" "</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">212 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"le "</span> <span class="o">+</span> <span class="n">myTime</span><span class="p">.</span><span class="n">substring</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span><span class="mi">10</span><span class="p">)</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">myTime</span><span class="p">.</span><span class="n">substring</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span><span class="mi">7</span><span class="p">)</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">myTime</span><span class="p">.</span><span class="n">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="s">" "</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">213 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"à "</span> <span class="o">+</span> <span class="n">myTime</span><span class="p">.</span><span class="n">substring</span><span class="p">(</span><span class="mi">11</span><span class="p">,</span><span class="mi">13</span><span class="p">)</span> <span class="o">+</span> <span class="s">"h"</span> <span class="o">+</span> <span class="n">myTime</span><span class="p">.</span><span class="n">substring</span><span class="p">(</span><span class="mi">14</span><span class="p">,</span><span class="mi">16</span><span class="p">)</span> <span class="o">+</span> <span class="s">" "</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">214 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">" : "</span> <span class="o">+</span> <span class="kr">String</span><span class="p">(</span><span class="n">mySpeed</span><span class="p">)</span> <span class="o">+</span> <span class="s">" km/h."</span><span class="p">)</span> <span class="p">;</span> <br /><span class="lineno">215 </span><br /><span class="lineno">216 </span><span class="p">}</span><br /><span class="lineno">217 </span><br /><span class="lineno">218 </span><span class="cm">/* --------------------------------------------------------------------------------------------------------</span><br /><span class="lineno">219 </span><span class="cm"> * SETUP : Initialisation</span><br /><span class="lineno">220 </span><span class="cm"> * -------------------------------------------------------------------------------------------------------- */</span><br /><span class="lineno">221 </span><span class="kr">void</span> <span class="nb">setup</span><span class="p">()</span> <span class="p">{</span><br /><span class="lineno">222 </span><br /><span class="lineno">223 </span> <span class="c1">// Initialisation de la liaison série, affichage 1er message</span><br /><span class="lineno">224 </span><br /><span class="lineno">225 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">begin</span><span class="p">(</span><span class="mi">115200</span><span class="p">);</span><br /><span class="lineno">226 </span> <span class="nf">delay</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">227 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">();</span> <br /><span class="lineno">228 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"-----------------------"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">229 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Exemple extraction JSON"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">230 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"-----------------------"</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">231 </span><br /><span class="lineno">232 </span> <span class="c1">// Tentative de connexion au Wi-Fi. Si la carte n'a pas réussi se connecter au dernier Point d'Accès connu,</span><br /><span class="lineno">233 </span> <span class="c1">// alors elle va se positionner en mode Point d'Accès, demandera sur l'adresse 192.168.4.1 quel nouveau</span><br /><span class="lineno">234 </span> <span class="c1">// Point d'Accès choisir. Par défaut, on restera bloqué tant que l'utilisateur n'aura pas fait de choix.</span><br /><span class="lineno">235 </span> <br /><span class="lineno">236 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Connexion au Wi-Fi ..."</span><span class="p">);</span><br /><span class="lineno">237 </span> <span class="k">if</span> <span class="p">(</span><span class="n">myWiFiManager</span><span class="p">.</span><span class="n">autoConnect</span><span class="p">(</span><span class="n">mySSID</span><span class="p">,</span> <span class="n">mySecKey</span><span class="p">))</span> <span class="p">{</span><br /><span class="lineno">238 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">();</span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">print</span><span class="p">(</span><span class="s">"Connecté ! Adresse IP : "</span><span class="p">);</span><br /><span class="lineno">239 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="nf">WiFi</span><span class="p">.</span><span class="nf">localIP</span><span class="p">());</span><br /><span class="lineno">240 </span> <span class="p">}</span><br /><span class="lineno">241 </span> <span class="k">else</span> <span class="p">{</span><br /><span class="lineno">242 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Connexion Wi-Fi KO :-("</span><span class="p">);</span> <br /><span class="lineno">243 </span> <span class="p">}</span><br /><span class="lineno">244 </span><br /><span class="lineno">245 </span> <span class="c1">// Initialisation du timer qui sera testé dans loop() - pour faire appel au serveur seulement toutes les 10 secondes </span><br /><span class="lineno">246 </span> <span class="c1">// millis() est une fonction système donnant le nombre de ms depuis le lancement ou la réinitialisation de la carte.</span><br /><span class="lineno">247 </span><br /><span class="lineno">248 </span> <span class="kr">unsigned</span> <span class="kr">long</span> <span class="n">myWakeUp</span> <span class="o">=</span> <span class="nf">millis</span><span class="p">()</span> <span class="o">+</span> <span class="n">TEN_SECONDS</span> <span class="p">;</span><br /><span class="lineno">249 </span><br /><span class="lineno">250 </span><span class="p">}</span><br /><span class="lineno">251 </span><br /><span class="lineno">252 </span><span class="cm">/* --------------------------------------------------------------------------------------------------------------</span><br /><span class="lineno">253 </span><span class="cm"> * LOOP : fonction appelée régulièrement par le système</span><br /><span class="lineno">254 </span><span class="cm"> * ------------------------------------------------------------------------------------------------------------- */</span><br /><span class="lineno">255 </span><span class="kr">void</span> <span class="nb">loop</span><span class="p">()</span> <span class="p">{</span> <br /><span class="lineno">256 </span><br /><span class="lineno">257 </span> <span class="kr">unsigned</span> <span class="kr">long</span> <span class="n">myNow</span> <span class="o">=</span> <span class="nf">millis</span><span class="p">()</span> <span class="p">;</span><br /><span class="lineno">258 </span> <span class="k">if</span> <span class="p">(</span><span class="n">myNow</span> <span class="o">>=</span> <span class="n">myWakeUp</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">259 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Wake Up ! Nouvelle demande au serveur ..."</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">260 </span> <span class="k">if</span> <span class="p">(</span><span class="n">serverRequest</span><span class="p">(</span><span class="n">Data_HOST</span><span class="p">,</span> <span class="n">Data_PORT</span><span class="p">,</span> <span class="n">Data_REQUEST</span><span class="p">,</span> <span class="n">Data_Response</span><span class="p">,</span> <span class="n">MAX_RESPONSE_SIZE</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span><br /><span class="lineno">261 </span> <span class="nf">Serial</span><span class="p">.</span><span class="nf">println</span><span class="p">(</span><span class="s">"Réponse reçue du serveur, lancement analyse JSON ..."</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">262 </span> <span class="n">showJSONAnswer</span><span class="p">(</span><span class="n">Data_Response</span><span class="p">,</span> <span class="n">MAX_RESPONSE_SIZE</span><span class="p">)</span> <span class="p">;</span><br /><span class="lineno">263 </span> <span class="p">}</span><br /><span class="lineno">264 </span> <span class="n">myWakeUp</span> <span class="o">=</span> <span class="n">myNow</span> <span class="o">+</span> <span class="n">TEN_SECONDS</span> <span class="p">;</span> <br /><span class="lineno">265 </span> <span class="p">}</span><br /><span class="lineno">266 </span><br /><span class="lineno">267 </span><span class="p">}</span><br /></pre></div><br /><br /><br/></nowiki>