POCL Parapluie connecté

De Les Fabriques du Ponant
(Redirigé depuis Parapluie connecté)
Aller à : navigation, rechercher

inspiré du travail de julien Levesque et d'Aurélien Fache voici un petit objet rigolo.

Connecté à la météo d'une ville choisie sur le site openweathermap, le parapluie et ouvert s'il pleut et il est fermé s'il ne pleut pas.

Comme la météo ne change pas souvent, il possède aussi une interface web permettant de l'activer à volonté !

Photographies du prototype n°1 :

Parapluieferme.jpg ParapluieOuvert.jpg

Parapluiewebtaille.gif

Matériel

  • Un D1 mini
  • Un servomoteur
  • Un parapluie Cocktail

pour bricoler :

  • du fil de fer,
  • un bouchon de liège,
  • un pistolet à colle,
  • du carton

ParapluieMontage.jpg

ParapluieFritzing.png

A améliorer

  • Modifier le code pour :
    • choisir son réseau wifi
    • choisir la ville
    • meilleur design web
  • créer une boitier à imprimer en 3D (à la place du carton)


code

  1 ///////////////
  2 // PARAPLUIE //
  3 ///////////////
  4 /*
  5  * A FAIRE :
  6  * installer wifiManager pour simplifier la connexion au wifi.
  7  * inclure dans une page web un formulaire pour saisir le code la ville - https://randomnerdtutorials.com/esp32-esp8266-input-data-html-form/ et 
  8  * https://circuits4you.com/2019/03/20/esp8266-receive-post-get-request-data-from-website/
  9  * enregistrer le code de la ville dans l'EEPROM - https://circuits4you.com/2016/12/16/esp8266-internal-eeprom-arduino/
 10  */
 11 /*  Un programme pour récupérer les données météo du site http://openweathermap.org/
 12  *  puis les afficher sur le moniteur série (partie de programme inspiré de http://educ8s.tv/esp8266-weather-display/)
 13  *  et ouvre ou ferme un parapluie chinois suivant les précipitations d'un lieu choisis (inspiré de http://julienlevesque.net/little-umbrella/ ) 
 14  *  Cette version intègre une page web de test du parapluie en hackant des bout de codes trouvés ici : https://www.ulasdikme.com/projects/esp8266/esp8266_ajax_websocket.php
 15  *  Antony Le Goïc-Auffret
 16  *  sous licence CC-By-SA - dimanche 1er août 2020 - Brest- Bretagne - France - Europe - Terre - Système solaire - Voie Lactée.
 17 
 18                                                                                         _
 19                                                                                       _| |_
 20                                                                                    _/ /  \  \_
 21                                                                                 _/   /    \    \_ 
 22                                                                              _/     /      \      \_ 
 23                                                                           _/  _._  /  _._   \ _._    \_
 24                                                                         / \_/     \_/  |_|\_/     \_/  \ 
 25                                                                                       /|#|
 26                                                                                      | | |
 27                                                                                      | | |
 28                                                                                      | | |
 29                                                                                      | | |
 30                                                                                      | | |  
 31                                                                                      | | |
 32                                                                                  ____|_\_/_
 33                                                                                 |    |     |
 34                                      BROCHAGE                                   |    |     |             
 35                                _________________                          ______|____|_____|__________
 36                               /     D1 mini     \                        |           |               |
 37               Non Attribué - |[ ]RST        TX[ ]| - Non Attribué        |   ___     |               |
 38               Non Attribué - |[ ]A0  -GPIO  RX[ ]| - Non Attribué        |  |_°_|    |               |
 39               Non Attribué - |[ ]D0-16    5-D1[ ]| - Non Attribué        | |     |   |               |
 40               Non Attribué - |[ ]D5-14    4-D2[ ]| - Non Attribué        | |     |   |               |
 41               Non Attribué - |[ ]D6-12    0-D3[X]| - gestion servo < _   | |   __|___|               |
 42               Non Attribué - |[ ]D7-13    2-D4[X]| - LED_BUILTIN       \ | | (o)_____/               |
 43               Non Attribué - |[ ]D8-15     GND[X]| - alim servo <-      || |Servo|                   |
 44               Non Attribué - |[ ]3V3 .      5V[X]| - alim servo <_|_    || |_____|                   |
 45                              |       +---+       |                |  \   \  |_°_|                    |
 46                              |_______|USB|_______|                  \  \ |\__/||                     |
 47             Le D1 mini est connecté en USB à votre ordinateur,        \ \|___/ |                     |
 48                         le moniteur série ouvert                       \_|____/                      |
 49                                                                          |___________________________|
 50 
 51 */
 52 #include <ESP8266WiFi.h>
 53 #include <ArduinoJson.h>
 54 #include <Servo.h> 
 55 #include <ESP8266WebServer.h>
 56 
 57 ESP8266WebServer server(80); //declaration du serveur web sur le port 80
 58 
 59 const char* nomDuReseau = "xxxxxxxxx";    // Nom du réseau wifi local (Ce qu'on appelle le SSID).
 60 const char* motDePasse = "yyyyyyyyy";                 // mot de passe du réseau wifi local
 61 String cledAPI = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; // clé de l'API du site http://openweathermap.org/
 62 // il faudra vous créer un compte et récupérer votre clé d'API, une formalité !
 63 // Sur le site, vous trouvez les identifiants de toutes les villes du monde.
 64 String identifiantVille = "6427109";                 //indiquez l'identifiant d'une ville (CityID), ici Brest en France
 65 /*Quelques identifiants d'autres villes françaises : 
 66  * 3030300, Brest
 67  * 6431033, Quimper
 68  * 6430976, Morlaix
 69  * 6453798, Lannion
 70  * 6453805, Saint-Brieuc
 71  * 6432801, Rennes
 72  * 6437298, Lorient
 73  * 2970777, Vannes
 74  * 6434483, Nantes
 75  * 6456407, Le Mans
 76  * 6427109, Caen
 77  * 6452361, Angers
 78  * 6456577, La Roche sur Yon
 79  * 3021411, Dieppe
 80  */
 81 
 82 WiFiClient client;
 83 char nomDuServeur[] = "api.openweathermap.org"; // Serveur distant auquel on va se connecter
 84 String resultat;
 85 
 86 int webClic = 0;
 87 unsigned long dateDernierChangement = 0;
 88 unsigned long dateCourante;
 89 unsigned long intervalle;
 90 
 91 Servo monservo;   // créer un objet "monservo" pour le contrôler
 92 
 93 String descriptionMeteo = "";
 94 String lieuMeteo = "";
 95 String Pays;
 96 float Temperature;
 97 float Humidite;
 98 float Pression;
 99 int para = 100;
100 int parastock;
101 int ferme = 90;  // angle pour fermer le parapluie
102 int ouvre = 170; // angle pour ouvrir le parapluie
103 bool pluie = 1;  // enregistre si il pleut ou pas.
104 
105 String webSite,javaScript,XML; //déclaration de variables
106 int start=0; // variable start
107 
108 void buildWebsite(){ // Fonction qui écrit le code html du site web
109   buildJavascript(); // appel de la fonction qui contruit le code javascript
110   webSite="<!DOCTYPE HTML>\n";
111   webSite+=javaScript; // insertion du javascript dans la page
112   webSite+="<HTML>\n";
113   webSite+="<style>\n";
114   webSite+="#button {\n";
115   webSite+="background-color: #E6E6FA;\n";
116   webSite+="border: none;\n";
117   webSite+="color: white;\n";
118   webSite+="padding: 32px;\n";
119   webSite+=" text-align: center;\n";
120   webSite+=" text-decoration: none;\n";
121   webSite+="display: inline-block;\n";
122   webSite+="font-size: 168px;\n";
123   webSite+="display:block;\n";
124   webSite+="margin:0 auto;\n";
125   webSite+="margin-top:130px;\n";
126   webSite+="cursor: pointer;\n";
127   webSite+="width:524px;\n";
128   webSite+="height:400px;\n";
129   webSite+="}\n";
130   
131   webSite+="p.thicker{font-weight:900;}\n";
132   webSite+="#runtime{font-weight:900; font-size: 147%; color:RED;}\n";
133   webSite+="</style>\n";
134   webSite+="<BODY bgcolor='#E6E6FA' onload='process()'>\n";
135 
136   webSite+="<button onClick='RunButtonWasClicked()' id='button'></button>  ";
137   webSite+="</BODY>\n";
138   webSite+="</HTML>\n";
139 }
140 
141 void buildJavascript(){ // Fonction qui contruit le code javascript
142   javaScript="<SCRIPT>\n";
143   
144   javaScript+="var xmlHttp=createXmlHttpObject();\n";
145   javaScript+="function createXmlHttpObject(){\n";
146   javaScript+=" if(window.XMLHttpRequest){\n";
147   javaScript+="    xmlHttp=new XMLHttpRequest();\n";
148   javaScript+=" }else{\n";
149   javaScript+="    xmlHttp=new ActiveXObject('Microsoft.XMLHTTP');\n";
150   javaScript+=" }\n";
151   javaScript+=" return xmlHttp;\n";
152   javaScript+="}\n";
153   
154   javaScript+="var click;\n";
155   
156   javaScript+="function handleServerResponse(){\n";
157   javaScript+="   xmlResponse=xmlHttp.responseXML;\n";
158   javaScript+="   xmldoc = xmlResponse.getElementsByTagName('response');\n";
159   javaScript+="   message = xmldoc[0].firstChild.nodeValue;\n";
160   javaScript+="if(message == 1){click = 1; message = 'ouvert'; document.getElementById('button').style.background='#FFA200';}else{click=0; message='ferm&eacute;'; document.getElementById('button').style.background='#111111';}\n";
161   javaScript+="   document.getElementById('button').innerHTML=message;\n";
162   javaScript+="}\n";
163 
164   javaScript+="function process(){\n";
165   javaScript+="   xmlHttp.open('PUT','xml',true);\n";
166   javaScript+="   xmlHttp.onreadystatechange=handleServerResponse;\n"; // no brackets?????
167   javaScript+="   xmlHttp.send(null);\n";
168   javaScript+=" setTimeout('process()',200);\n";
169   javaScript+="}\n";
170 
171   javaScript+="function process2(){\n";
172   javaScript+="    xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
173   javaScript+="    xmlHttp.send(null);\n";
174   javaScript+=" setTimeout('process2()',400);\n";
175   javaScript+="}\n";
176 
177   javaScript+="function RunButtonWasClicked(){\n";
178   javaScript+="click = (click==1)?0:1;\n";
179   javaScript+="    xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
180   javaScript+="    xmlHttp.send(null);\n";
181   javaScript+="}\n";       
182 
183   javaScript+="</SCRIPT>\n";
184 }
185 
186 uint16_t x; 
187 String data; // variable data qui sert à ?
188 
189 void buildXML(){
190   XML="<?xml version='1.0'?>";
191   XML+="<response>";
192   XML+=data;
193   XML+="</response>";
194 }
195 
196 
197 void handleWebsite(){ // génère le site web
198   buildWebsite();     // écriture du html
199   server.send(200,"text/html",webSite); // mise en ligne du site
200 }
201 
202 void handleXML(){ // gère le xml (description de l'état du boutton)
203   buildXML();
204   server.send(200,"text/xml",XML);
205 }
206 
207 void handle1ESPval(){ 
208   start = server.arg("Start").toFloat();
209 }
210 
211 int start2=0;
212 int inc=0;
213 
214 void setup() {
215   Serial.begin(9600);
216   Serial.println();
217   connexion();
218   delay(100);
219   server.on("/",handleWebsite);
220   server.on("/xml",handleXML);
221   server.on("/set1ESPval",handle1ESPval);
222   
223   server.begin(); 
224   prendDonneesMeteo();
225   parapluie (); 
226 }
227 
228 void loop() {
229   dateCourante = millis();
230   intervalle = dateCourante - dateDernierChangement; // interval de temps depuis la dernière mise à jour du parapluie
231   
232   if (intervalle >= 600000) //Récupère de nouvelles données toutes les 10 minutes
233     {
234       dateDernierChangement = millis();
235       prendDonneesMeteo();   // récupère les données météo
236       parapluie();           // met à jour le parapluie
237       //monservo.detach();     // débrancher le servomoteur de la broche D3 (GPIO 0)
238     }
239     
240    if (!start == webClic)   // si l'état du bouton web à changé
241      {
242      parastock = para;      // stock la valeur "para" dans "parastock"
243      if (start)
244        {
245         para = 100;         // triche sur la valeur "para" pour un test pluie
246         parapluie();        // met à jour le parapluie
247         Serial.println("parapluie fermé ");
248        }
249      if (!start)
250        {
251         para = 900;        // triche sur la valeur "para" pour un test pluie
252         parapluie();       // met à jour le parapluie
253         Serial.println("parapluie ouvert ");
254        }
255      webClic = start;      //met à jour webClic
256      para = parastock;     // redonne à para sa valeur initiale
257      }
258 
259    data =(String)start;
260    server.handleClient();
261 
262    if ((intervalle%6000) == 0){ // toutes les 6 secondes, j'écris ces infos sur le moniteur série 
263      ecritMeteoGeneral(lieuMeteo, descriptionMeteo);
264      Serial.print("interval modulo 6000 : "); Serial.println((intervalle%60));
265      Serial.print("interval : "); Serial.println(intervalle);
266      Serial.print("date Courante : ");Serial.println(dateCourante);
267      Serial.print("date du Dernier Changement : ");Serial.println(dateDernierChangement);
268    }
269 }
270 
271 void prendDonneesMeteo() //Fonction qui utilise le client web du Wemos pour envoyer/recevoir des données de requêtes GET.
272 {
273   if (client.connect(nomDuServeur, 80)) {  // Démarre la connexion du client, recherche les connexions
274     client.println("GET /data/2.5/weather?id=" + identifiantVille + "&units=metric&lang=fr&APPID=" + cledAPI);
275     client.println("Host: api.openweathermap.org");
276     client.println("User-Agent: ArduinoWiFi/1.1");
277     client.println("Connection: close");
278     client.println();
279   }
280   else {
281     Serial.println("Echec de la connexion"); //message d'erreur si la connexion échoue
282     Serial.println();
283   }
284 
285   while (client.connected() && !client.available()) delay(1); // attend les données
286   while (client.connected() || client.available()) {          // soit le client est connecté, soit il a des données disponibles
287     char c = client.read();  // récupère les données
288     resultat = resultat + c; // les agrège dans la chaine de caractère "resultat" 
289   }
290 
291   client.stop(); // stoppe le client
292   resultat.replace('[', ' ');
293   resultat.replace(']', ' ');
294   Serial.println(resultat); // écrit la chaine de caractère en entier sur le moniteur série
295 
296   char tableauJson [resultat.length() + 1];
297   resultat.toCharArray(tableauJson, sizeof(tableauJson));
298   tableauJson[resultat.length() + 1] = '\0';
299 
300  StaticJsonDocument<1024> doc;
301 
302  DeserializationError error = deserializeJson(doc, tableauJson);
303 
304 if (error) {
305     Serial.print(F("deserializeJson() failed with code "));
306     Serial.println(error.c_str());
307     return;
308 }
309 
310   String lieu = doc["name"];
311   String pays = doc["sys"]["country"];
312   float temperature = doc["main"]["temp"];
313   float humidite = doc["main"]["humidity"];
314   String meteo = doc["weather"]["main"];
315   String description = doc["weather"]["description"];
316   String id = doc["weather"]["id"];                   //récupère le chiffre identifiant "id" de l'état météo sous forme de texte.
317   float pression = doc["main"]["pressure"];
318 
319   descriptionMeteo = description;
320   lieuMeteo = lieu;
321   Pays = pays;
322   Temperature = temperature;
323   Humidite = humidite;
324   Pression = pression; 
325   para =id.toInt(); //transforme le texte "id" en entier.
326 
327 }
328 
329 void ecritMeteoGeneral(String lieu, String description)
330 {
331   Serial.println("------------------");
332   Serial.print(lieu);
333   Serial.print(", ");
334   Serial.print(Pays);
335   Serial.print(", ");
336   Serial.print(description);
337   Serial.print(", ");
338   Serial.println(para);
339 }
340 
341 void parapluie ()
342 {
343   
344 if (para<600) {            // Si la valeur de l'indicateur météo est inférieur à 600 c'est qu'il pleut.
345   if (pluie == 0) {        // Si avant ça il ne pleuvait pas
346     monservo.attach(0);    // brancher le servomoteur sur la broche D3 (GPIO 0)
347     monservo.write(ouvre); // ouvre le parapluie
348     Serial.print("ouvre à : ");Serial.println(ouvre);
349     pluie = 1;             // note qu'il pleut
350     Serial.print("pluie à : ");Serial.println(pluie);
351   }
352 }
353 else {                     // si il ne pleut pas
354   if (pluie == 1) {        // et que juste avant il pleuvait (le parapluie était donc ouvert).
355     monservo.attach(0);    // brancher le servomoteur sur la broche D3 (GPIO 0)
356     monservo.write(ferme); // ferme le parapluie
357     Serial.print("ferme à : ");Serial.println(ferme);
358     pluie = 0;             // note bien qu'il ne pleut pas
359     Serial.print("pluie à : ");Serial.println(pluie);
360   }
361 }
362 delay (200);
363 monservo.detach();     // débrancher le servomoteur de la broche D3 (GPIO 0)
364 }
365 
366 void connexion() {
367   WiFi.mode(WIFI_STA); // Le Wemos est en mode station wifi.
368   Serial.println("Connexion");
369   WiFi.begin(nomDuReseau, motDePasse);
370   while (WiFi.status() != WL_CONNECTED) {
371     delay(500);
372     Serial.print(".");
373   }
374   Serial.println("Connecté");
375   Serial.print("Station IP address: ");
376   Serial.println(WiFi.localIP());
377   pinMode(LED_BUILTIN, OUTPUT);   // Configure la broche D4 (GPIO 2) sur la quelle est branchée la LED_BUILTIN en sortie
378   digitalWrite(LED_BUILTIN, LOW); // Allume la LED_BUILTIN  
379 }

Parapluieweb.gif

Prototype n°2 du POCL-Parapluie

Prototype n°2

Les fichiers à imprimer :

Toutes les pièces : Fichier:1-Parapluie-pieces-finales.stl

Le porte-servo : Fichier:1-Porte-servo.stl

Le porte-carte : Fichier:1-Porte-carte.stl

Les rouages : Fichier:1-Rouages.stl


Matériel supplémentaire :

- 1 ombrelle à cocktail

- 1 cure-dents

- 3 câbles Dupont

- De la colle

!!! Le porte-carte maintient le support de la crémaillère droit, afin que les rouages fonctionnent, respecter l'ordre des éléments !!!


Code

  • Téléchargement du code :
  • Paramètres à modifier au besoin :
// ssid AP : concaténation POCL_NAME + "-" + POCL_ID

const char *POCL_ID = "1";                // id unique du POCL à paramétrer
const char *POCL_NAME = "POCL-Parapluie"; // nom du POCL

// mot de pas AP: minimun 8 caractères
const char *AP_PW = "lpdgo2021";

#define CLEF_API  "remplacemoi"           // Clef de l'API par default à remplacer ici ou via le site
#define ID_VILLE "3030300"                // ID ville par default à remplacer ici ou via le site

/*Quelques identifiants d'autres villes françaises :
 * 3030300, Brest
 * 6431033, Quimper
 * 6430976, Morlaix
 * 6453798, Lannion
 * 6453805, Saint-Brieuc
 * 6432801, Rennes
 * 6437298, Lorient
 * 2970777, Vannes
 * 6434483, Nantes
 * 6456407, Le Mans
 * 6427109, Caen
 * 6452361, Angers
 * 6456577, La Roche sur Yon
 * 3021411, Dieppe
 */

#define ANGLE_FERME 0 // angle pour fermer le parapluie
#define ANGLE_OUVERT 170 // angle pour ouvrir le parapluie


Boîte pour le Pocl Parapluie :

Murs, haut et bas à découper sur du medium 5mm d'épaisseur, face avant à découper en plexiglas de 5mm :

Erreur lors de la création de la miniature :
Boîte à Pocl n°1
Face en plexiglas