POCL Parapluie connecté

De Les Fabriques du Ponant
Révision datée du 16 octobre 2023 à 12:12 par Antonydbzh (discussion | contributions) (code)
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

<syntaxhighlight lang="Arduino" line> /////////////// // PARAPLUIE // /////////////// /*

* A FAIRE :
* installer wifiManager pour simplifier la connexion au wifi.
* inclure dans une page web un formulaire pour saisir le code la ville - https://randomnerdtutorials.com/esp32-esp8266-input-data-html-form/ et 
* https://circuits4you.com/2019/03/20/esp8266-receive-post-get-request-data-from-website/
* enregistrer le code de la ville dans l'EEPROM - https://circuits4you.com/2016/12/16/esp8266-internal-eeprom-arduino/
*/

/* Un programme pour récupérer les données météo du site http://openweathermap.org/

*  puis les afficher sur le moniteur série (partie de programme inspiré de http://educ8s.tv/esp8266-weather-display/)
*  et ouvre ou ferme un parapluie chinois suivant les précipitations d'un lieu choisis (inspiré de http://julienlevesque.net/little-umbrella/ ) 
*  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
*  Antony Le Goïc-Auffret
*  sous licence CC-By-SA - dimanche 1er août 2020 - Brest- Bretagne - France - Europe - Terre - Système solaire - Voie Lactée.
                                                                                       _
                                                                                     _| |_
                                                                                  _/ /  \  \_
                                                                               _/   /    \    \_ 
                                                                            _/     /      \      \_ 
                                                                         _/  _._  /  _._   \ _._    \_
                                                                       / \_/     \_/  |_|\_/     \_/  \ 
                                                                                     /|#|
                                                                                    | | |
                                                                                    | | |
                                                                                    | | |
                                                                                    | | |
                                                                                    | | |  
                                                                                    | | |
                                                                                ____|_\_/_
                                                                               |    |     |
                                    BROCHAGE                                   |    |     |             
                              _________________                          ______|____|_____|__________
                             /     D1 mini     \                        |           |               |
             Non Attribué - |[ ]RST        TX[ ]| - Non Attribué        |   ___     |               |
             Non Attribué - |[ ]A0  -GPIO  RX[ ]| - Non Attribué        |  |_°_|    |               |
             Non Attribué - |[ ]D0-16    5-D1[ ]| - Non Attribué        | |     |   |               |
             Non Attribué - |[ ]D5-14    4-D2[ ]| - Non Attribué        | |     |   |               |
             Non Attribué - |[ ]D6-12    0-D3[X]| - gestion servo < _   | |   __|___|               |
             Non Attribué - |[ ]D7-13    2-D4[X]| - LED_BUILTIN       \ | | (o)_____/               |
             Non Attribué - |[ ]D8-15     GND[X]| - alim servo <-      || |Servo|                   |
             Non Attribué - |[ ]3V3 .      5V[X]| - alim servo <_|_    || |_____|                   |
                            |       +---+       |                |  \   \  |_°_|                    |
                            |_______|USB|_______|                  \  \ |\__/||                     |
           Le D1 mini est connecté en USB à votre ordinateur,        \ \|___/ |                     |
                       le moniteur série ouvert                       \_|____/                      |
                                                                        |___________________________|
  • /
  1. include <ESP8266WiFi.h>
  2. include <ArduinoJson.h>
  3. include <Servo.h>
  4. include <ESP8266WebServer.h>

ESP8266WebServer server(80); //declaration du serveur web sur le port 80

const char* nomDuReseau = "xxxxxxxxx"; // Nom du réseau wifi local (Ce qu'on appelle le SSID). const char* motDePasse = "yyyyyyyyy"; // mot de passe du réseau wifi local String cledAPI = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"; // clé de l'API du site http://openweathermap.org/ // il faudra vous créer un compte et récupérer votre clé d'API, une formalité ! // Sur le site, vous trouvez les identifiants de toutes les villes du monde. String identifiantVille = "6427109"; //indiquez l'identifiant d'une ville (CityID), ici Brest en France /*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
*/

WiFiClient client; char nomDuServeur[] = "api.openweathermap.org"; // Serveur distant auquel on va se connecter String resultat;

int webClic = 0; unsigned long dateDernierChangement = 0; unsigned long dateCourante; unsigned long intervalle;

Servo monservo; // créer un objet "monservo" pour le contrôler

String descriptionMeteo = ""; String lieuMeteo = ""; String Pays; float Temperature; float Humidite; float Pression; int para = 100; int parastock; int ferme = 90; // angle pour fermer le parapluie int ouvre = 170; // angle pour ouvrir le parapluie bool pluie = 1; // enregistre si il pleut ou pas.

String webSite,javaScript,XML; //déclaration de variables int start=0; // variable start

void buildWebsite(){ // Fonction qui écrit le code html du site web

 buildJavascript(); // appel de la fonction qui contruit le code javascript
 webSite="<!DOCTYPE HTML>\n";
 webSite+=javaScript; // insertion du javascript dans la page
 webSite+="\n";
  webSite+="\n";
  webSite+="\n";

  webSite+="  ";
  webSite+="\n";
  webSite+="\n";

}

void buildJavascript(){ // Fonction qui contruit le code javascript

 javaScript="<SCRIPT>\n";
 
 javaScript+="var xmlHttp=createXmlHttpObject();\n";
 javaScript+="function createXmlHttpObject(){\n";
 javaScript+=" if(window.XMLHttpRequest){\n";
 javaScript+="    xmlHttp=new XMLHttpRequest();\n";
 javaScript+=" }else{\n";
 javaScript+="    xmlHttp=new ActiveXObject('Microsoft.XMLHTTP');\n";
 javaScript+=" }\n";
 javaScript+=" return xmlHttp;\n";
 javaScript+="}\n";
 
 javaScript+="var click;\n";
 
 javaScript+="function handleServerResponse(){\n";
 javaScript+="   xmlResponse=xmlHttp.responseXML;\n";
 javaScript+="   xmldoc = xmlResponse.getElementsByTagName('response');\n";
 javaScript+="   message = xmldoc[0].firstChild.nodeValue;\n";
 javaScript+="if(message == 1){click = 1; message = 'ouvert'; document.getElementById('button').style.background='#FFA200';}else{click=0; message='fermé'; document.getElementById('button').style.background='#111111';}\n";
 javaScript+="   document.getElementById('button').innerHTML=message;\n";
 javaScript+="}\n";
 javaScript+="function process(){\n";
 javaScript+="   xmlHttp.open('PUT','xml',true);\n";
 javaScript+="   xmlHttp.onreadystatechange=handleServerResponse;\n"; // no brackets?????
 javaScript+="   xmlHttp.send(null);\n";
 javaScript+=" setTimeout('process()',200);\n";
 javaScript+="}\n";
 javaScript+="function process2(){\n";
 javaScript+="    xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
 javaScript+="    xmlHttp.send(null);\n";
 javaScript+=" setTimeout('process2()',400);\n";
 javaScript+="}\n";
 javaScript+="function RunButtonWasClicked(){\n";
 javaScript+="click = (click==1)?0:1;\n";
 javaScript+="    xmlHttp.open('SET','set1ESPval?Start='+click,true);\n";
 javaScript+="    xmlHttp.send(null);\n";
 javaScript+="}\n";       
 javaScript+="</SCRIPT>\n";

}

uint16_t x; String data; // variable data qui sert à ?

void buildXML(){

 XML="<?xml version='1.0'?>";
 XML+="<response>";
 XML+=data;
 XML+="</response>";

}


void handleWebsite(){ // génère le site web

 buildWebsite();     // écriture du html
 server.send(200,"text/html",webSite); // mise en ligne du site

}

void handleXML(){ // gère le xml (description de l'état du boutton)

 buildXML();
 server.send(200,"text/xml",XML);

}

void handle1ESPval(){

 start = server.arg("Start").toFloat();

}

int start2=0; int inc=0;

void setup() {

 Serial.begin(9600);
 Serial.println();
 connexion();
 delay(100);
 server.on("/",handleWebsite);
 server.on("/xml",handleXML);
 server.on("/set1ESPval",handle1ESPval);
 
 server.begin(); 
 prendDonneesMeteo();
 parapluie (); 

}

void loop() {

 dateCourante = millis();
 intervalle = dateCourante - dateDernierChangement; // interval de temps depuis la dernière mise à jour du parapluie
 
 if (intervalle >= 600000) //Récupère de nouvelles données toutes les 10 minutes
   {
     dateDernierChangement = millis();
     prendDonneesMeteo();   // récupère les données météo
     parapluie();           // met à jour le parapluie
     //monservo.detach();     // débrancher le servomoteur de la broche D3 (GPIO 0)
   }
   
  if (!start == webClic)   // si l'état du bouton web à changé
    {
    parastock = para;      // stock la valeur "para" dans "parastock"
    if (start)
      {
       para = 100;         // triche sur la valeur "para" pour un test pluie
       parapluie();        // met à jour le parapluie
       Serial.println("parapluie fermé ");
      }
    if (!start)
      {
       para = 900;        // triche sur la valeur "para" pour un test pluie
       parapluie();       // met à jour le parapluie
       Serial.println("parapluie ouvert ");
      }
    webClic = start;      //met à jour webClic
    para = parastock;     // redonne à para sa valeur initiale
    }
  data =(String)start;
  server.handleClient();
  if ((intervalle%6000) == 0){ // toutes les 6 secondes, j'écris ces infos sur le moniteur série 
    ecritMeteoGeneral(lieuMeteo, descriptionMeteo);
    Serial.print("interval modulo 6000 : "); Serial.println((intervalle%60));
    Serial.print("interval : "); Serial.println(intervalle);
    Serial.print("date Courante : ");Serial.println(dateCourante);
    Serial.print("date du Dernier Changement : ");Serial.println(dateDernierChangement);
  }

}

void prendDonneesMeteo() //Fonction qui utilise le client web du Wemos pour envoyer/recevoir des données de requêtes GET. {

 if (client.connect(nomDuServeur, 80)) {  // Démarre la connexion du client, recherche les connexions
   client.println("GET /data/2.5/weather?id=" + identifiantVille + "&units=metric&lang=fr&APPID=" + cledAPI);
   client.println("Host: api.openweathermap.org");
   client.println("User-Agent: ArduinoWiFi/1.1");
   client.println("Connection: close");
   client.println();
 }
 else {
   Serial.println("Echec de la connexion"); //message d'erreur si la connexion échoue
   Serial.println();
 }
 while (client.connected() && !client.available()) delay(1); // attend les données
 while (client.connected() || client.available()) {          // soit le client est connecté, soit il a des données disponibles
   char c = client.read();  // récupère les données
   resultat = resultat + c; // les agrège dans la chaine de caractère "resultat" 
 }
 client.stop(); // stoppe le client
 resultat.replace('[', ' ');
 resultat.replace(']', ' ');
 Serial.println(resultat); // écrit la chaine de caractère en entier sur le moniteur série
 char tableauJson [resultat.length() + 1];
 resultat.toCharArray(tableauJson, sizeof(tableauJson));
 tableauJson[resultat.length() + 1] = '\0';
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, tableauJson);

if (error) {

   Serial.print(F("deserializeJson() failed with code "));
   Serial.println(error.c_str());
   return;

}

 String lieu = doc["name"];
 String pays = doc["sys"]["country"];
 float temperature = doc["main"]["temp"];
 float humidite = doc["main"]["humidity"];
 String meteo = doc["weather"]["main"];
 String description = doc["weather"]["description"];
 String id = doc["weather"]["id"];                   //récupère le chiffre identifiant "id" de l'état météo sous forme de texte.
 float pression = doc["main"]["pressure"];
 descriptionMeteo = description;
 lieuMeteo = lieu;
 Pays = pays;
 Temperature = temperature;
 Humidite = humidite;
 Pression = pression; 
 para =id.toInt(); //transforme le texte "id" en entier.

}

void ecritMeteoGeneral(String lieu, String description) {

 Serial.println("------------------");
 Serial.print(lieu);
 Serial.print(", ");
 Serial.print(Pays);
 Serial.print(", ");
 Serial.print(description);
 Serial.print(", ");
 Serial.println(para);

}

void parapluie () {

if (para<600) { // Si la valeur de l'indicateur météo est inférieur à 600 c'est qu'il pleut.

 if (pluie == 0) {        // Si avant ça il ne pleuvait pas
   monservo.attach(0);    // brancher le servomoteur sur la broche D3 (GPIO 0)
   monservo.write(ouvre); // ouvre le parapluie
   Serial.print("ouvre à : ");Serial.println(ouvre);
   pluie = 1;             // note qu'il pleut
   Serial.print("pluie à : ");Serial.println(pluie);
 }

} else { // si il ne pleut pas

 if (pluie == 1) {        // et que juste avant il pleuvait (le parapluie était donc ouvert).
   monservo.attach(0);    // brancher le servomoteur sur la broche D3 (GPIO 0)
   monservo.write(ferme); // ferme le parapluie
   Serial.print("ferme à : ");Serial.println(ferme);
   pluie = 0;             // note bien qu'il ne pleut pas
   Serial.print("pluie à : ");Serial.println(pluie);
 }

} delay (200); monservo.detach(); // débrancher le servomoteur de la broche D3 (GPIO 0) }

void connexion() {

 WiFi.mode(WIFI_STA); // Le Wemos est en mode station wifi.
 Serial.println("Connexion");
 WiFi.begin(nomDuReseau, motDePasse);
 while (WiFi.status() != WL_CONNECTED) {
   delay(500);
   Serial.print(".");
 }
 Serial.println("Connecté");
 Serial.print("Station IP address: ");
 Serial.println(WiFi.localIP());
 pinMode(LED_BUILTIN, OUTPUT);   // Configure la broche D4 (GPIO 2) sur la quelle est branchée la LED_BUILTIN en sortie
 digitalWrite(LED_BUILTIN, LOW); // Allume la LED_BUILTIN  

}

<syntaxhighlight>

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