POCL Parapluie connecté : Différence entre versions

De Les Fabriques du Ponant
Aller à : navigation, rechercher
Ligne 6 : Ligne 6 :
  
 
[[Fichier:Parapluieferme.jpg|300px]] [[Fichier:ParapluieOuvert.jpg|300px]]
 
[[Fichier:Parapluieferme.jpg|300px]] [[Fichier:ParapluieOuvert.jpg|300px]]
 +
==Matériel==
 +
* 1 D1 mini
 +
* Un servomoteur
 +
* Un parapluie Cocktail
 +
 +
pour bricoler :
 +
* du fil de fer,
 +
* un bouchon de liège,
 +
* un pistolet à colle,
 +
* du carton
 +
 +
==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)
  
[[Fichier:Parapluieweb.gif]]
 
  
 
==code==
 
==code==
Ligne 392 : Ligne 408 :
  
 
</pre>
 
</pre>
 +
 +
[[Fichier:Parapluieweb.gif]]

Version du 29 mars 2021 à 16:22

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 ouvdert s'il pleut et il est fermé s'il ne pleut pas.

Comme la météo ne change pas souvent, il a aussi une interfaceweb pour le tester !

Parapluieferme.jpg ParapluieOuvert.jpg

Matériel

  • 1 D1 mini
  • Un servomoteur
  • Un parapluie Cocktail

pour bricoler :

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

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

///////////////
// 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 wemos est connecté en USB à votre ordinateur,        \ \|___/ |                     |
                        le moniteur série ouvert                       \_|____/                      |
                                                                         |___________________________|

*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Servo.h> 
#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+="<HTML>\n";
  webSite+="<style>\n";
  webSite+="#button {\n";
  webSite+="background-color: #E6E6FA;\n";
  webSite+="border: none;\n";
  webSite+="color: white;\n";
  webSite+="padding: 32px;\n";
  webSite+=" text-align: center;\n";
  webSite+=" text-decoration: none;\n";
  webSite+="display: inline-block;\n";
  webSite+="font-size: 168px;\n";
  webSite+="display:block;\n";
  webSite+="margin:0 auto;\n";
  webSite+="margin-top:130px;\n";
  webSite+="cursor: pointer;\n";
  webSite+="width:524px;\n";
  webSite+="height:400px;\n";
  webSite+="}\n";
  
  webSite+="p.thicker{font-weight:900;}\n";
  webSite+="#runtime{font-weight:900; font-size: 147%; color:RED;}\n";
  webSite+="</style>\n";
  webSite+="<BODY bgcolor='#E6E6FA' onload='process()'>\n";

  webSite+="<button onClick='RunButtonWasClicked()' id='button'></button>  ";
  webSite+="</BODY>\n";
  webSite+="</HTML>\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  
}

Parapluieweb.gif